Reputation: 3861
I think I have a pretty good understanding of ARC and the proper use cases for selecting an appropriate lifetime qualifiers (__strong
, __weak
, __unsafe_unretained
, and __autoreleasing
). However, in my testing, I've found one example that doesn't make sense to me.
As I understand it, both __weak
and __unsafe_unretained
do not add a retain count. Therefore, if there are no other __strong
pointers to the object, it is instantly deallocated (with immutable strings being an exception to this rule). The only difference in this process is that __weak
pointers are set to nil, and __unsafe_unretained
pointers are left alone.
If I create a __weak
pointer to a simple, custom object (composed of one NSString property), I see the expected (null) value when trying to access a property:
Test * __weak myTest = [[Test alloc] init];
myTest.myVal = @"Hi!";
NSLog(@"Value: %@", myTest.myVal); // Prints Value: (null)
Similarly, I would expect the __unsafe_unretained
lifetime qualifier to cause a crash, due to the resulting dangling pointer. However, it doesn't. In this next test, I see the actual value:
Test * __unsafe_unretained myTest = [[Test alloc] init];
myTest.myVal = @"Hi!";
NSLog(@"Value: %@", myTest.myVal); // Prints Value: Hi!
Why doesn't the __unsafe_unretained
object become deallocated?
[EDIT]: The object is being deallocated... if I try to substitute lines 2 - 3 with NSLog(@"%@", myTest);
the app crashes (and an overridden dealloc
in Test
is being called immediately after the first line). I know that immutable strings will continue to be available even with __unsafe_unretained
, and that a direct pointer to the NSString would work. I am just surprised that I could set a property on a deallocated object (line 2), and that it could later be dereferenced from a pointer to the deallocated object it belonged to (line 3)! If anyone could explain that, it would definitely answer my question.
Upvotes: 2
Views: 2513
Reputation: 9
Nothing strange there… You need to have at least 1 strong reference to object to keep it alive.
Test * anTest = [[Test alloc] init];
Test * __weak myTest = anTest;
myTest.myVal = @"Hi!";
NSLog(@"Value: %@", myTest.myVal); // Prints Value: (Hi)
Upvotes: 0
Reputation: 53000
I am just surprised that I could set a property on a deallocated object (line 2), and that it could later be dereferenced from a pointer to the deallocated object it belonged to (line 3)! If anyone could explain that, it would definitely answer my question.
When the object is deallocated it is not zeroed. As you have a pointer to the deallocated object and the property value is stored at some offset to that pointer it is possible that storing and retrieving that property value will succeed after deallocation, it is also quite possible that everything will blow up for some reason or other.
That your code works is quite fragile, try debugging it with "Show Disassembly While Debugging" and stepping through, you'll probably hit an access violation, or take down Xcode itself...
You should never be surprised that strange things happen in C, Objective-C, C++ or any of the family; instead reserve your surprise for so few strange things happening!
Upvotes: 3
Reputation: 4520
Because the constant string in objc is a constant pointer to a heap address and the address is still valid.
edited after comment:
Maybe because the memory at the test objects address hasn't been overwritten and still contains that object? Speculating....
Upvotes: 2
Reputation: 162712
@"hi!"
produces a static global constant string instance that is, effectively, a singleton. Thus, it'll never be deallocated because it wasn't really allocated in the first place (at least, it really isn't a normal heap allocation).
Anytime you want to explore object lifespan issues, always use a subclass of NSObject both to guarantee behavior and to make it easy to drop in logging hooks by overriding behavior.
Upvotes: 1
Reputation: 25619
You can see when Test
is deallocated by implementing its -dealloc
method and adding some simple logging.
However, even if Test is deallocated immediately, the memory it occupied in RAM may remain unchanged at the time you call myVal
.
Upvotes: 1