I use below code to test arc and help to understand the ARC
NSArray __strong* myArray = [NSArray arrayWithObjects:#"123", nil];
NSArray __weak* yourArray = myArray;
NSArray __unsafe_unretained* theirArray = yourArray;
myArray = nil;
NSLog(#"yourArray = %#, theirArray = %#", yourArray, theirArray);
As my understand, the log should be:yourArray = (null), theirArray = (null)
In face the log is:
yourArray = (
123
), theirArray = (
123
)
if I change the code and remove the __unsafe_unretained:
NSArray __strong* myArray = [NSArray arrayWithObjects:#"123", nil];
NSArray __weak* yourArray = myArray;
//NSArray __unsafe_unretained* theirArray = yourArray;
myArray = nil;
NSLog(#"yourArray = %#", yourArray);
the log is right:yourArray = (null)
why if I add __unsafe_unretained local variables to weak reference my NSArray object, it like retain or strong my NSArray object.
Any one can help to answer the doubt.
Best Regards
The compiler is pretty good about figuring out what object's lifetimes are during this kind of simple example with limited scope. It can tell that it shouldn't release the array until the end of the scope is reached (or at least the end of the last read is reached with these simple assignments).
If you use an ivar that's marked __unsafe_unretained, assign it after the weak assignment, and build with full optimizations, you might see different results, even in this kind of simple case. Also, basically while the compiler may be able to deal with situations like this, where the __weak variable is still set and the __unsafe_unretained variable is still able to be read without disaster, the point is that you can't count on it to behave that way. What you can count on is that a __weak variable will be nil'd out after its last __strong reference goes away, and that the compiler won't insert retain/release directives for an __unsafe_unretained variable. As long as you follow the rules, you will have predictable results. As soon as you don't follow the rules, anything that happens or doesn't happen is undefined... so it may work in the simulator and iPod 4G's but fail horribly on 4S and 5 while working half the time on any iPad... the point is, the result is undefined.
The object returned by [NSArray arrayWithObjects:#"123", nil] is autoreleased and won't be deallocated until the autorelease pool has been flushed. The weak reference won't be nil'd out until the object is deallocated. The unsafe_unretained reference will never be nil'd out and will become invalid once the object is deallocated.
So the log should be
yourArray = ( 123 ), theirArray = ( 123 )
But later when the object gets deallocated, then it should be
yourArray = ( NULL ), theirArray = undefined
undefined because the pointer is invalid and could point to a new object or just garbage, it could even crash the code
Related
I have an array defined in the form of a constant, outside any function:
const VALUES: [(char, &str); 2] = [('A', "abc"), ('B', "acb")];
I'm attempting to use the find() method located on the Iterator, in order to extract a single element from an array based on a predicate:
VALUES.iter().find(|&&(name, _)| name == 'A');
In this form, it works fine. I am, however, unable to evaluate the found element into anything, as as soon as I attempt creating a let binding, trying to bind the result, which, according to the docs should come back as an Option<T>.
Let's change the second line to that which doesn't work:
const VALUES: [(char, &str); 2] = [('A', "abc"), ('B', "acb")];
fn main() {
let result = VALUES.iter().find(|&&(name, _)| name == 'A');
}
(Playground)
One would expect this to return the Option<T> as according to the docs, but instead I get back a compilation error:
error: borrowed value does not live long enough
--> src/main.rs:4:63
|
4 | let result = VALUES.iter().find(|&&(name, _)| name == 'A');
| ------ temporary value created here ^ temporary value dropped here while still borrowed
5 | }
| - temporary value needs to live until here
|
= note: consider using a `let` binding to increase its lifetime
I'm utterly confused; I'm certain I've just messed up with the "borrow checker". Perhaps someone can point me in the right direction?
The problem is in the specific way you're turning that array into an iterator.
First of all, consts in Rust do not actually exist anywhere. Instead, they're substituted by-value everywhere they're used. So every time you use a constant, you're getting a new copy of it.
Secondly, you're using IntoIterator::into_iter. This takes the subject by value and converts it into an iterator.
These combine with the third part: IntoIterator is not implemented for fixed-size arrays. It's only implemented for pointers to fixed-size arrays. So, in order to invoke into_iter, the compiler has to insert an automatic borrow of the invocant.
So, what's actually happening is this:
let t = {
// Make a new copy of `VALUES`.
let values: [(char, &str); 5] = VALUES;
// Borrow it.
let values: &[_] = &values;
// Call `into_iter` on the borrow.
IntoIterator::into_iter(values).find(|&&(name, _)| name == 'A')
};
This causes problems because the compiler has to simultaneously copy and borrow VALUES in order to get an iterator. Like all temporaries, said copy only lives as long as the statement, but the borrow (by being bound to a variable) has to live longer than that.
The best solution is to make VALUES a pointer to the array. This prevents copying the entire array; instead, you only copy the pointer on each use.
const VALUES: &[(char, &str)] = &[...];
Alternately, you could explicitly make a copy of VALUES and store it in a variable, then use into_iter on that. However, like before, this will introduce unnecessary copying.
DK.'s answer is correct, but I'd suggest a simpler change to get your code to work — use static instead of const.
From the documentation about static and const:
More specifically, constants in Rust have no fixed address in memory.
static items aren’t inlined upon use. This means that there is only one instance for each value, and it’s at a fixed location in memory.
Switching to static allows you to get a memory address of the thing you are iterating over.
static VALUES: [(char, &str); 2] = [('A', "abc"), ('B', "acb")];
fn main() {
let result = VALUES.iter().find(|&&(name, _)| name == 'A');
}
Here, result is an Option<&(char, &str)>.
I noticed something interesting when using for-in constructs in cfscript: It appears that the variable in struct argument in the for(in) loop is set as an independent variable, and has no reference to its parent array key.
If you run the following code you will see the array doesn't change on output. The variable local.i inside the loop is being assigned the new value, but the array remains unchanged.
function arrayTest()
{
local.foo = ["bar-one","bar-two", "bar-three"];
for (local.i in local.foo)
{
local.i = "I am #local.i#";
// Dump local.i; its output will be 'I am bar-[one[two]] etc...'
}
// Dump local.i; its output will as above
// Dump the array; its keys remain unchanged: "bar-one, bar-two, -bar-three"
writeDump(local.foo);
}
So why is this? I know arrays are passed by reference in CF, but I'm not passing an array here. I'm just using one in a for-in construct. There is a difference, no?
It's a bit misleading to call the argument variable in structure. I see local.i as a shortcut to local.foo[ local.i ]. It sounds like the var is indeed the array key and we can modify it. The solution here is to use a plain for() loop, instead.
I would not expect the underlying array to change unless i was a complex object of some sort or something passed by reference. For example if foo were
local.foo = [{a="b"},{a="c"}];
then modifying local.i.a = "I am key #local.i.a#"; would modify the object within the array, and those changes would be reflected in your dump.
Update:
Ultimately this comes down to pointers or references. In loose terms, local.i is just a pointer to objects within the array. So resetting local.i just points that variable at some other object in memory. It has no impact on the array. Notice the change in hashcode value?
// example
local.foo = [ "bar-one" ];
for (local.i in local.foo)
{
WriteOutput("local.i (before) =#local.i.hashCode()#<br>"); //-335192660
WriteOutput("local.foo[1] = #local.foo[1].hashCode()#<br>");//-335192660
local.i = "I am key #local.i#";
WriteOutput("local.i (after) = #local.i.hashCode()#<br>"); //1075915694
}
writeDump(local.foo);
Curious question:
Take this function:
function something():Array
{
var ar:Array = [];
var i:MovieClip;
for each(i in list)
ar[ar.length] = i.x;
return ar;
}
Will ar be discarded, or does it continue to chill in my memory, adding to the memory being used each time I call this function?
My question applies to each of these cases:
var val:Array = something(); (obviously val is stored in memory, but what about the original array created in the function?)
something();
Would it maybe be safer to always do this instead?:
return ar.slice();
Garbage Collection is done automatically by the flash engine. However it is not done instantaneously. In flash, anything that is not referenced will be GC'ed.
[See http://www.adobe.com/devnet/flashplayer/articles/garbage_collection.html for more details]
So for your function case of 'something()'
for(var i:int = 0; i<100; i++) {
something();
}
Your 100 or so arrays generated will 'chill' for probably a few ms (varies) before getting cleared up by GC. However...
var stupidArr:Array = [];
for(var i:int = 0; i<100; i++) {
stupidArr.push( something() );
}
Your 100 or so arrays WILL STAY, as long as the variable 'stupidArr' exists. However if this occurs next.
stupidArr = null;
OR
stupidArr.pop(); //Looped as desired
As long as the array in your function 'something()' is not refrenced (cleared with each pop, or complete null). By a variable accessible to you. The item will be free'd for GC. And hence will leave the memory.
So "Array.slice()" for your function is actually a bad idea, cause it doubles the memory consumption before GC (affects performance)
On another note, if lets say your function has useless variables, for eg: Your loop counters. These too are GC'ed if they are not refrenced at the end of the day. For flash, hence variable and garbage collection is 'pretty easy'. Just know this rule of thumb.
If by any means possible, you can programically access the variable, it will persist. If you cant, it will be destroyed.
As far as I know var ar:Array = [] is local to the function something() and will be GC'ed when the function exits. Unless you pick it up when the function returns as you suggest in case 1: var val:Array = something();. Calling something(); should not accumulate memory.
Edit:
I found this link, while digging into this; check the PowerPoint.
If you only run that function once, the array will actually stay in memory. However, any allocation thereafter might trigger the Garbage Collector to run and it should eventually remove ar. Which still suggests that the variable ar will not, in the long run, hang around something() and clog your memory.
ActionScript's Array and Vector classes both have a slice() method. If you don't pass any parameters, the new Array or Vector is a duplicate (shallow clone) of the original Vector.
What does it mean to be a "shallow clone"? Specifically, what is the difference between
Array newArray = oldArray.slice();
Vector.<Foo> newVector = oldVector.slice();
and
Array newArray = oldArray;
Vector.<Foo> newVector = oldVector;
? Also, what if the Vector's base type isn't Foo, but something simple and immutable like int?
Update:
What is the result of the following?
var one:Vector.<String> = new Vector.<String>()
one.push("something");
one.push("something else");
var two:Vector.<String> = one.slice();
one.push("and another thing");
two.push("and the last thing");
trace(one); // something, something else, and another thing
trace(two); // something, something else, and the last thing
Thanks! ♥
In your context, what .slice() does is simply to make a copy of your vector, so that newArray refers to a different object from oldArray, except both seem like identical objects. Likewise goes for newVector and oldVector.
The second snippet:
Array newArray = oldArray;
Vector.<Foo> newVector = oldVector;
actually makes newArray a reference to oldArray. That means both variables refer to the same array. Same for newVector and oldVector — both end up referring to the same vector. Think of it as using a rubber stamp to stamp the same seal twice on different pieces of paper: it's the same seal, just represented on two pieces of paper.
On a side note, the term shallow copy differs from deep copy in that shallow is a copy of only the object while deep is a copy of the object and all its properties.
Also, what if the Vector's base type isn't Foo, but something simple and immutable like int?
It's the same, because your variables refer to the Vector objects and not their ints.
What is the result of the following?
Your output is correct:
something, something else, and another thing
something, something else, and the last thing
two = one.slice(), without any arguments, makes a new copy of one with all its current contents and assigns it to two. When you push each third item to one and two, you're appending to distinct Vector objects.
This concept seems to trouble me. Why does an NSError object need its pointer passed to a method that is modifying the object? For instance, wouldn't just passing a reference to the error do the same thing?
NSError *anError;
[myObjc doStuff:withAnotherObj error:error];
and then in doStuff:
- (void)doStuff:(id)withAnotherObjc error:(NSError *)error
{
// something went bad!
[error doSomethingToTheObject];
}
Why doesn't the above work like most other object messaging patterns work? Why must instead we use error:(NSError **)error?
Quite simply:
if you pass a pointer to an object to your function, the function can only modify what the pointer is pointing to.
if you pass a pointer to a pointer to an object then the function can modify the pointer to point to another object.
In the case of NSError, the function might want to create a new NSError object and pass you back a pointer to that NSError object. Thus, you need double indirection so that the pointer can be modified.
The NSError** pattern is used when a method normally returns some value but instead may need to return an error object (of type NSError*) if it fails. In Objective-C a method can only return one type of object, but this is a case where you want to return two. In C-like languages when you need to return an extra value you ask for a pointer to a value of that type, so to return an NSError* you need an NSError** parameter. A more realistic example would be this:
// The method should return something, because otherwise it could just return
// NSError* directly and the error argument wouldn't be necessary
- (NSArray *)doStuffWithObject:(id)obj error:(NSError **)error
{
NSArray *result = ...; // Do some work that might fail
if (result != nil) {
return result;
} else {
// Something went bad!
// The caller might pass NULL for `error` if they don't care about
// the result, so check for NULL before dereferencing it
if (error != NULL) {
*error = [NSError errorWithDomain:...];
}
return nil; // The caller knows to check error if I return nil
}
}
If you only had an NSError* parameter instead of an NSError** then doStuff would never be able to pass the error object back to its caller.
An old question, but still I think its worth putting this here -
The actual culprit is NSError. If you look at its class reference, there are no setter methods for any of its attributes, i.e. domain, code or userInfo. So there is no way, you can just alloc and initialize a NSError, pass it to the method and then populate information on the passed NSError object. (Had there been a setter method, we could have just passed a NSError * and done something like error.code = 1 in the method.)
So in case there is an error, you have to generate a new NSError object in the method and if you are doing so the only way to pass it back to the caller is by having a NSError ** argument. (For the reason explained in the above answers.)
Alternate statement of what n8gray said:
Because you're not receiving an object to send messages to; you're creating the object and returning it. You generally need the pointer-to-an-NSError *-variable argument because you can only use the return statement on one thing at a time, and you're already using it with NO.
I still did not get the full picture by reading all the answers above. The layman exercise I did below, finally helped me understand what is happening. Just putting it out there in case it helps other beginners.
Suppose you have following
#interface Class X
-(void) methodX:(NSMutableArray *)array;
#end
In some other part of the code you have the following sequence
ClassX *objectX = [[ClassX alloc] init];
NSMutableArray *arrayXX = [#[#(1), #(2)] mutableCopy];
//What is stored in arrayXX is the address in the heap at which the NSMutableArray object starts, lets call this address ZZZ
//array starting at address ZZZ in the heap now contains NSNUmbers #1,#2
[objectX methodX:array]
When you invoke [objectX methodX:array], what is being received by the method is a copy of array. Since array contains an address (i.e is a pointer), the copy is special in that what is received is another variable with address ZZZ in it.
So, if methodX does [array removeObjectAtIndex:0], then the object starting at address ZZZ gets affected (now only contains one NSNUmber #(2)). So, when the method returns, the original array also gets affected.
Suppose instead methodX does array = [#[#(2)] mutableCopy]; then original array does not get affected. This is because you did not go into address ZZZ and change something. Instead you overwrote the ZZZ in the copy received by the method to a different address YYY. YYY address is the start of a NSMUtableArray object with one element NSNUmber #(2). The original ZZZ address still contains a NSMUtableArray with two elements. #(1) and #(2). So, when method returns, the original array is unaffected.