Reference not returning nil even if deallocated - iphone

I have run into a real hard one here.
My class is self-created (a + (void) start;-method) which is sent a delegate.
I need to send the delegate a few messages through delegate-selectors. To check if the delegate was released, I have tried if (delegate == nil/NULL), but even if it really is released, it still says it wasn't.
How do I go around to fix this? (delegate is assigned to an id)
This is how my app is built up:
AppDelegate
> NavController
>> TableView
>>> Post
>>>> GetData
GetData is the self-created class. Post is the delegate of GetData, and is released by TableView/NavController. After releasing Post, it is also set to nil.
In other words, GetData does not release it's delegate!

Deallocating the content of a pointer does not set the pointer to NULL automatically. You need to do it explicitly, e.g.
[delegate release];
delegate = nil;

You can't check if an object was released. Another object could've come along and occupied the same memory space, becoming indistinguishable with the original object.
The user of a class with a delegate is responsible for setting the delegate to nil when the delegate object is released. It's not detectable by the object itself.

Related

NSDictionary retain crash

I have a NSDictionary, storing value by parsing. I am allocating and storing value in -viewDidLoad. And accessing the values in -viewWillAppear. It works perfectly fine.
Then, I have UIPageControl with UIScrollView. While scrolling the UIScrollView, again I am accessing the same dictionary, it crashes saying
[CFString respondToSelector:]: send to deallocated....
- (void) viewDidLoad {
[super viewDidLoad];
scoresDict = [[NSDictionary alloc] initWithDictionary:[scoreObj scores]];
}
- (void)loadScrollViewWithPage:(int)page {
if (page < 0)
return;
if (page >= kNumberOfPages)
return;
NSLog(#“scoresDict %#”,scoresDict);
}
I tried using retain in the same function, it didn’t work out. And copy, it also didn’t work. Any help would be appreciated. Thanks!
You don't say so, but it looks like all of these methods are being called from an instance of your custom UIViewController or UIScrollViewController subclass.
The most likely problem is that this instance itself isn't being retained.
When you first load the view from the nib, both -viewDidLoad and -viewWillAppear are called. It's possible, however, that garbage collection is happening in between those calls and your call to -loadScrollViewWithPage, and that no object has any connection to the view controller instance itself.
If that's the case, using copy or retain on the scoresDict won't solve the problem. You need to make sure that you are copying or retaining the view controller instance itself. Figure out what object needs to be retaining your view controller and make sure it is being retained.
A quick way to test whether this is the problem or not: create a "myViewController" property in your application delegate. In viewDidLoad, add a line:
[[[UIApplication sharedApplication] delegate] setMyViewController:self];
If this fixes your problem, it means the problem was the view controller getting released. That doesn't mean this is your best solution, though--it's just a diagnostic. You need to figure out what should be retaining your view controller and make sure it does so.

Deallocated in delegate paradigm

I have ParentViewController that allocates ChildViewController, pushes it onto controller stack and releases it.
ChildViewController implements the protocol ProductDownloadDelegateProtocol required by the Product class.
At some point, ChildViewController creates a Product object and sets itself as its downloadDelegate.
While downloading, Product class updates ChildViewController via methods defined in ProductDownloadDelegateProtocol.
If the user presses the back button in the navBar of ChildViewController while downloading, the next update of download percentage from Product causes an EXC_BAD_ACCESS.
Although Product checks if downloadDelegate is nil, problem still occurs since ChildViewController/downloadDelegate is deallocated, but not set as nil. I don't know which point is best to set ChildViewController to nil.
Is my design wrong?
If your ChildViewController creates an instance of Product and sets itself as the delegate, it should be its responsibility to remove itself as the delegate when it's about to be unloaded. Either in it's viewDidUnload or dealloc method you should be setting the Product delegate to nil.
If ChildViewController stays around (say you are reusing the view controller), maybe you can remove it as the delegate in the viewWillDissappear method.
Another solution to fix this particular EXC_BAD_ACCESS issue is to move to ARC and use Zeroing Weak References (see a good writeup here, http://www.mikeash.com/pyblog/friday-qa-2010-07-16-zeroing-weak-references-in-objective-c.html).
Still, I would recommend you move to ARC for the right reasons, and not to fix this particular issue you are facing.
My rule of thumb goes like this: You should never be the delegate of an object you do not own. Here, "own" means "hold a strong reference to" (in ARC terms). The main exception is when the delegate retains you, and UIApplication.delegate because that's a bit weird.
Usually I bundle the logic into the setter like this:
-(void)setProduct:(Product*)p
{
product.delegate = nil;
[product release];
product = [p retain];
product.delegate = self;
}
-(void)dealloc
{
self.product = nil;
}
However, an underlying problem with this design is that a "product download" can only have one delegate. What if you navigate away from the ChildViewController and then back into (a new instance of) it? Are they different Product instances, both being downloaded?
A better way might be to have a download manager singleton (as much as I hate singletons) that manages downloads and uses NSNotification/NSNotificationCenter for progress notifications.

iOS check if delegate exists before call method

I write iOS app and use imageStore library to lazy load images and cache them in memory. (https://github.com/psychs/imagestore)
On ViewController I create imagestore instance:
imageStore = [ImageStore new];
imageStore.delegate = self;
When image loaded successfuly, imagestore call delegate method
- (void)imageStoreDidGetNewImage:(ImageStore*)sender url:(NSString*)url
that doing reloadData on tableview to redraw cells.
All works good. But there is the problem: if ViewController didUnload (go back in navigation controller) and image loaded, application finish with crash, because imagestore call method of unloaded ViewController.
I try to do following:
1) in ViewController I place this code in viewDidUnload section:
imageStore.delegate = nil;
imageStore = nil;
2) In imageStore I added checking for nil:
if(delegate != nil) {
...call delegate method
}
It works, but periodically app crash anyway.
Try putting this code on dealloc section.
imageStore.delegate = nil;
imageStore = nil;
In the same way the if clause is not necessary because any call to an nil object is ignored by the application, so if you have something like this:
id delegate = nil;
[delegate callAnyMethod];
has no effect in your application behavior, in other hand if the call of the method delegate is optional you should asure that delegate responds to selector, something like this should do the trick:
if([delegate conformsToProtocol:#protocol(yourProtocolName)] && [delegate respondsToSelector:#selector(imageStoreDidGetNewImage:url:)]) {
[delegate imageStoreDidGetNewImage:imageStore url:url];
}
Cheers!
It works, but periodically app crash anyway.
That's a contradiction. There are two possibilities:
Your fix worked, and the app is crashing for some other reason.
Your fix did not work, the app continues to crash for the same reason it was crashing before.
It's hard to know what's wrong without knowing which of these two possibilities is in fact happening. Look at the error message and the evidence from the crash, such as the stack crawl. Why is the app crashing? Does it try to dereference the delegate property somewhere without checking it first? Does it depend on the delegate doing something, so that if the delegate no longer exists that thing doesn't get done and that in turn leads to a crash? These are the kinds of things I'd look for, but again the most important thing is to start with the evidence you have and follow your nose.

View controller release before delegate return

I have a problem. My view controller (ViewController) implement a delegate method of a object (DataPuller, data get from the internet). DataPuller will retrieve data on the internet without blocking user interaction with the view. But when I navigate between screen, in some cases, that ViewController release before DataPuller return the list of objects. The DataPuller return, it checks:
if (delegate && [delegate respondsToSelector:#selector(getCommentDidDownloadFinish:)]) {
[self.delegate performSelector:#selector(getCommentDidDownloadFinish:) withObject:self];
}
And the application crash here because ViewController release, it becomes a zombie object. Does anyone have this problem before and how to solve it? I think another way is using NSNotification, but I wonder any other better solutions. Any ideas, solutions are welcomes. Thanks.
Your view controller must remove itself as the DataPuller delegate at some point. Typicially, this is handled in the dealloc method:
- (void)dealloc {
dataPuller.delegate = nil;
[dataPuller release];
[super dealloc];
}
You may also decide to do this in -viewDidUnload or -viewDidDisappear:.
Delegation (usually) implies some sort of ownership - i.e., if you make an object a delegate of another object, usually the delegate object holds a strong reference (i.e., retains) the delegating object.
As an example, a UITableViewController is the delegate of its UITableView. This is okay, because the controller retains the tableview through the "view" property.
If your design does not allow ownership, use notifications, like you already suggested. As a bonus, notifications can signal multiple listeners if you would ever need that.
Don't forget to remove your observer in the dealloc of the view controller!

UIViewController is popped from view stack and NSURLConnection crashes the application

I am pushing a UIViewController onto a UINavigationController. This view controller immediately starts a download of an xml feed and then parses it. However, if you hit the back button before it is done downloading, and crashes with EXC_BAD_ACCESS. The line that is crashing it is in parserDidEndDocument and is this line:
if (self.delegate && [self.delegate conformsToProtocol:#protocol(ModelDelegate)]) [self.delegate modelDidFinishParsing:self];
I assume it is crashing because it is trying to access self.delegate which is not assigned anymore. How do I get around this?
Also, I would release the model object in the modelDidFinishParsing method. How would I release this model if it never reaches this method.
I set up objects to handle my downloads (and other asynchronous or long running tasks) in the AppDelegate, then trigger them as required from various controllers. That way they are owned and have persistence through the life of the application.
The best way to do this is to pass them to the viewControllers that will need them (rather than the viewController "expecting" the appDelegate to have such and such an object ready and waiting) - dependency injection.
These objects update my model in some way when they finish and if I need to, I use NSNotifications to announce they are done. This isolates me from the mess I used to get into trying to cancel or swap delegates in viewWillDisappear etc to avoid the kind of issues you are running into.
The reason your app is crashing is probably because NSURLConnection retains its delegate (so it can call back to it reliably) but objects that this delegate has weak references to have been deallocated.
Ie, in your case what self.delegate points to has probably been deallocated when the view controller is popped but the delegate property has not been cleared (set to nil).
The solution to your problem is to clear (nil) self.delegate at the appropriate time when the UIViewController subclass is being popped off the navigation stack.
Note: retaining delegates is not usual behaviour for Cocoa classes. In situations where it happens contrary to standard practice it is documented (see the NSURLConnection docs).