I had a library designed for ARC and iOS 5, but a customer wanted to build for iOS 4.x. I converted properties with the weak qualifer to unsafe_unretained, however now it seems I am running into memory corruption-esq crashes.
In the case of the crash, I have a delegate property like this
#property (unsafe_unretained) id<MYDelegateProtocol> delegate;
and I #synthesize the ivar.
whenever I want to call a method on the delegate, I check if it is nil, and if it is not, then I call the method, since I have no optional methods in that protocol, I don't check respondsToSelector.
However, since changing the weak to unsafe_unretained, I have found that apparently the reference goes away, and I am left with a dangling pointer. If I put a breakpoint on ObjC Exceptions, (for unrecognized message), and then po the address of the object, I see that it is an object that is not even implementing the protocol of the delegate, so of course it crashes.
When using unsafe_unretained how can I know that the reference has "gone away"? My code relied on the pointer being zeroed out before.
You have an object ownership problem. An object that has been released won't be nil, it would just be pointing at the address where the object used to be. An nil pointer would point to 0x0. You need to analyze how come your architecture has a delegate that is being released while the record being delegated is alive. On some special circumstances, objects being delegated might retain their delegates (like NSURLConnection). On other cases, objects being delegated are a property of their delegates, in which case, the delegation needs to be cleared out before deallocation. Analyze your pattern and/or provide more information.
Try this class I developed iOSWeakForwarder
When using unsafe_unretained how can I know that the reference has
"gone away"? My code relied on the pointer being zeroed out before.
This has to be determined at compile time by you. Often, the object that is the delegate should set the delegate property of the object delegating to it to nil in the dealloc method. For example:
- (void)dealloc
{
if (_httpRequest.delegate == self)
_httpRequest.delegate = nil;
}
Hope this helps!
Related
I have a #class Foo which contains a __weak id bar ivar. Several actions from methods in different classes can cause the object to disappear and thus get bar niled.
I want to perform an action when the ivar is automatically niled by ARC.
If possible, I would want to avoid turning bar into a property or using Key-Value Observing.
Is this even possible? If not, can KVO be used against non-property ivars?
I was led here by a duplicate question, here is what I answered:
You can't do that with KVO, but you can still get a notification and emulate this by associating an object with your iVar using objc_setAssociatedObject(), it will be deallocated when the weak variable dies.
#interface WeakObjectDeathNotifier : NSObject
#end
#implementation WeakObjectDeathNotifier
- (void)dealloc
{
// the code that shall fire when the property will be set to nil
}
#end
You can build on top of that very elaborate notifiers, using NSNotificationCenter or just custom blocks, depending on how heavily you rely on that for a specific ivar case or for lots of them.
The good thing about this solution is that it works with any __weak ivar, even if you don't control the type the __weak ivar has.
KVO cannot be successfully used on non-property IVARs.
You cannot detect from the runtime when Objective-C's ARC nils an IVAR.
I suggest to override dealloc. If you know the type of the object that will be allocated, and it's a custom class (otherwise subclass it), you can perform the action when the object is deallocated, which is exactly what happens when ARC sets the retain count to zero and sets the weak variable to nil.
Very short snippet:
#pragma mark - NSNetServiceBrowserDelegate
- (void)netServiceBrowser:(NSNetServiceBrowser *)netServiceBrowser didFindService:(NSNetService *)netService moreComing:(BOOL)moreServicesComing
{
netService.delegate = self;
[netService resolveWithTimeout:30];
}
In this example - netServiceWillResolve: is called in the same event loop. Then, netService is released (because of ARC) and rest have no chance to be called.
I don't like the solution with ivar. NSNetService * neither array. Is there better approach to "retain" netService until last delegate method is called?
The documentation explicitly says that the delegate is not retained and that is the normal pattern in Cocoa. Also the documentation on resolveWithTimeout: says nothing about the NSNetService being retained until the delegate method has been called. Under normal memory management rules (and I think this goes for ARC too) you can't make any assumptions about the lifetime of that object outside the scope of that method.
Or to put it another way, by not maintaining a strong reference to the object, you are saying "I'm not interested in this anymore, do away with it if you like".
It's now more than 5 months that I'm in Objective-C, I've also got my first app published in the App Store, but I still have a doubt about a core functionality of the language.
When am I supposed to use self accessing iVars and when I'm not?
When releasing an outlet you write self.outlet = nil in viewDidUnload, instead in dealloc you write [outlet release]. Why?
When you write self.outlet = nil the method [self setOutlet:nil]; is called. When you write outlet = nil; you access variable outlet directly.
if you use #synthesize outlet; then method setOutlet: is generated automatically and it releases object before assigning new one if you declared property as #property (retain) NSObject outlet;.
Very very important blog to understand about properties getter-setter method in objective c
Understanding your (Objective-C) self
http://useyourloaf.com/blog/2011/2/8/understanding-your-objective-c-self.html
You use self when you are refering to a #property.
Usually it will have been #synthesize'd.
You do not use self if you are refering to a "private" variable. Typically, I use properties for UI elements such as UIButtons or for elements I want easily reachable from other classes.
You can use the #private, #protected modifiers to explicitly enforce visibility. You cannot however use private methods, that do not exist in Objective-C.
The part about nil, release and dealloc is unrelated to the use of "self". You release what you retained, you nil what is autoretained.
You should read the Objective-C guide, it's well written and very enlightening.
You use self. when you're accessing properties of class that you're in (hence self). Basically you use self when you want to retain a value, but is only when you have retain in your property definition.
release just releases object that you've retained. You shouldn't release something that you haven't retained cuz it will lead to crash (zombie object).
I have this property synthesized and declared in my class 'ClassA'
#interface ClassA
#property (nonatomic, retain) NameFieldCell* nameCell;
#end
I know that the rule says that the nameCell property should be released in my dealloc method when it is declared with retain|copy|...
However, 'ClassA' gets instantiated lazily and sometimes the nameCell property is not even used, which means that I don't use its setter method nor access it nor retain it explicitly.
Should I still be calling [nameCell release] in my dealloc method? I find it difficult to understand that I should be releasing something that is not even initialized. And since it is not initialized, the reference counter is 0 and makes no sense to release it? Or is nameCell somehow retained automatically when instantiating 'ClassA' even if I am not making use of it?
In Objective-C, the memory allocated by alloc for a class is initialized to all-bits-zero on allocation (and then the isa ivar is set), which results in nameCell's backing ivar being set to nil by default. And since it is not an error to send a message to nil in Objective-C (the message is just ignored), you are free to just call [nameCell release] without worrying about whether nameCell was ever set.
If nameCell is nil, then [nameCell release] has no effect. This makes it easy to cover both cases, so yes, you should release it in your dealloc method.
That or start using ARC, and you won't have to worry about it.
I have seen code (probably Apple's own sample code) written in such a way where it releases the ivar in dealloc and setting the property in viewDidUnload.
e.g.
- (void)viewDidUnload
{
self.navigationController = nil;
}
- (void)dealloc
{
[_navigationController release];
}
Why do them in two places? Also, why set nil in one and release in another. It seems that self.property = nil would just take care of everything since it would release and set the ivar to nil.
You're right: you can indeed do self.property = nil everywhere, including dealloc. The only downside is that if the setter method does anything more complicated than just releasing the ivar, you might end up trying to access other fields that have already been released, etc.
As for why you also release the outlet in viewDidUnload, that's a memory optimization. Since the stuff you release in viewDidUnload are things that will be reinstantiated when the view is loaded again, releasing them there frees up memory in low-memory situations.
Apple recomments that you not call setters in the init and especially dealloc routines.
This is due to the fact that the object is only partially set up at this time, and setters could have observers attached to them, or could be overridden by subclasses, and otherwise have undesirable affects during dealloc, or could be confused during init with a partially configured object.
Hence, you normally use:
_navigationController = [[NavController alloc] init];
style code in your init routine,
[_navigationController release];
style code in your dealloc, and setters in other code where the object is known to be fully complete.
Some cases to consider:
Subclass overrides setNavigationController and references its own ivars allocated by init. Crash on init.
Subclass overrides setNavigationController and references its own ivars released in dealloc. Crash on dealloc.
Subclass overrides setNavigationController and redraws some parts of the screen. Pointless waste of cycles, or glitchy display.
Other objects being deallocated at the same time observe navigationController and those observers fire during dealloc
etc
If you're relying on garbage collection (available in Objective-C 2.0) then setting the ivar to nil and calling release will achieve the same end.
I'm guessing that the code you're looking at is managing the memory with release and only setting it to nil so that you don't later try to access an object that is no longer there. The nil is more bookkeeping than memory management when you're not relying on the GC.