I'm confused on Delegates. I was always under the impression that if you didn't set a delegate than it wouldn't respond to delegate callbacks. I have a crash with a Class method that releases its object.
Lets say I have a object "Something" that has a delegate set-up for handling results to the callers that care. Something uses ASIHTTPRequest to do some posts / gets asynchronously.
-(void)somethingHasResults{
//something needs to tell its listeners that its has completed (pseudo-code)
if([delegate respondsToSelector:#selector(somethingDidComplete)]){
[delegate somethingDidComplete];
}
}
But I also have a Helpers.h/.m with a series of class methods like so...
+(void)shoutItOutLoud{
//doesn't need a delegate, doesn't need a response -- just do your thing and exit
Something *something = [[Something alloc] init];
[something shouldDoSomethingAwesomeButDoesntNeedToRespond];
[something release];
}
When I use [Helpers shoutItOutLoud] I get a crash. Now the crash is actually in ASIHttpRequest.m's reportFinished, but following the trace brings me all the way back to my class method which is releasing the object before completion.
The whole point of this is that I'm surprised that I have a crash here and I'm trying to wrap my head around this. A co-worker and I got into a discussion and he says its because I'm releasing my object so the delegate is pointing at garbage. This seems wrong to me, as this class method doesn't receive the responses anyways?
I believe your problem is actually to do with not cleaning the ASIHttpRequest delegate: an ASIHttpRequest object sets its delegate to the object is called from.
So you will need to clear that delegate as well (in the Something class). Assuming that you have a property of type ASIHttpRequest, here is what I do in the dealloc method:
- (void)dealloc {
...
[_request clearDelegatesAndCancel];
[_request release];
[super dealloc];
}
Related
so i start a ASIFormDataRequest on my [viewDidLoad] in a UIViewController.
ASIFormDataRequest *detailRequest = [ASIFormDataRequest requestWithURL:url];
detailRequest.delegate = self;
[detailRequest startAsynchronous];
If my UIViewController gets released before my Request finishes, my app crashes.
If i add my ASIFormDataRequest as an instance variable for example
#property(nonatomic, retain) ASIFormDataRequest *detailRequest;
and nil the delegate on dealloc
-(void)dealloc {
if(self.detailRequest != nil) { self.detailRequest.delegate = nil; }
self.detailRequest = nil;
[super dealloc];
}
the app no longer crashes.
but i don't think it's necessary to create a instance variable just for this, especially if i have multiple requests.
is there a better way to do this?
I usually create an array and store all active requests in the array. When the request is completed I remove the request, and when the controller calls dealloc I cancel all of the requests and nil the delegate.
In order to release it you must have a pointer to it so yes, use an ivar. iars are not expensive.
By doing self.detailRequest = [ASIFormDataRequest requestWithURL:url]; I am guessing it is creating an autorelease object whose lifespan isn't bound to your controller class. If the creation and deletion of your object is bound to your controller, it's logical to use a instance variable.
More details about autorelease
You could do this:
detailRequest.delegate = [self retain];
and then call
[self autorelease];
In the ASIFormDataRequest callback method. That's what I generally tend to do, anyway.
That way, the request object retains its delegate for the duration of the request.
As this is the Asynchronous request so if you set delegate it means as soon as response comes your delegate methods will be called. Till that time your object should be alive to handle the response. So making it retain and releasing in the dealloc is fine and before than that you have to set delegate to nil. So that if response comes after releasing the method, framework should not be misguided to search for method of dead object.
To handle multiple request the best way is to create the array and number of objects you want to use. When you are done with the objects, in dealloc method iterate through each object and set delegate nil and release the object.
I have a class whose sole purpose is to go download a specific file off the net, store it locally and then return the local path of the stored file.
I use this class based on whether I have a local copy of the file in question or not, and I sometimes call it up multiple times at the same time, if more than one file needs downloading. The way I use it is simply
Loader *l = [[Loader alloc] initWithDelegate:self];
[l downloadFile:someFile];
[l release];
The thing is, in order to keep it around until it's done downloading, I am actually doing [self retain]; in the class, then [self autorelease]; when it's done. This feels hacky though. How do people deal with this?
I agree that [self release] and [self autorelease] feel weird, but that doesn't mean they're wrong. In some situations, they can be the right thing to use (and I've used them before).
If you want to avoid them, however, you might consider making a LoaderManager class that simply owns the Loader objects until they're done loading stuff. It'd essentially be a wrapper around an array, and you'd use it like this (or something):
#interface LoaderManager : NSObject {
NSMutableSet *loaders;
}
#end
#implementation LoaderManager
- (id)init {
self = [super init];
if (self) {
loaders = [[NSMutableSet alloc] init];
}
return self;
}
- (void)dealloc {
[loaders release];
[super dealloc];
}
- (void)addLoader:(Loader *)loader {
[loaders addObject:loader];
}
#end
And then your Loader object would do:
[myLoader downloadFile:someFile manager:aLoaderManager];
Which internally would simply invoke:
[aLoaderManager addLoader:self];
Under the circumstances, I think it's fine for your Loader to retain and autorelease itself. The most expedient solution might be to just add a detailed comment to your code that explains why it does what it does. The biggest problem is that Loader takes a delegate. Delegators don't usually retain their delegates in order to avoid retain cycles, but in this case it seems possible that the delegate could be deallocated before the Loader is finished downloading its file. If that happens, a crash is likely. So, if you want to continue with this fire-and-forget style, you might want to have Loader retain its delegate.
What is the recommended way of doing this. Should I call super dealloc first or last or doesn't it matter?
- (void)dealloc
{
[super dealloc];
[orderNumber release];
[orderDate release];
}
Also when it comes to overriding methods like didViewLoad - should I call super first or last?
Always call [super dealloc] last or you might easily come into trouble because you're working on a stale object.
With didViewLoad you normally call it before your own code as you want the standard initialization stuff executed before. I've seen examples in Apple's code that don't call the super implementation at all, though, so maybe there's not much going on anyway.
In this case call the super after you have released all your properties/iVars. For viewDidLoad/willAppear/etc. I usually call the super first. The order matters when your custom class is relying on an object that is created by the super. For the default viewDidLoad this is not the case so it is preference(I believe).
There is no general rule - you chose to override the method, what does it do? Do you want it to happen before or after your custom implementation?
didViewLoad doesn't appear to be a real method.
We know that [super dealloc] destroys the current object completely, so any code that comes after it is wrong. So, in this case, call [super dealloc] last.
The pointers orderNumber and orderDate are held inside your object.
[super dealloc] deallocates your object (aka self).
Once you deallocate your object you must not rely on the things inside it (e.g. orderNumber) having the values they did before you deallocated it.
Therefore, deallocate the members before deallocating the self object.
The opposite holds true for init functions - you can't initialise the pointers until after your object is constructed, so [super init] comes before you initialise the members.
Regarding viewDidLoad (et al), you do whatever works. If you have stuff you want to happen before the superclass does its thing, then you do it before you call the superclass method, and likewise for stuff you want to happen afterwards.
If you don't know whether your code should run before or after, then it probably doesn't matter.
I'm trying to create a class which will let me get requested data from a web service. I'm stuck on how to return the values.
// FooClass.m
// DataGrabber is the class which is supposed to get values
dataGrabber = [[DataGrabber alloc] init];
xmlString = [dataGrabber getData:[NSDictionary dictionaryWithObjectsAndKeys:#"news", #"instruction", #"sport", #"section", nil]];
In this example, it's supposed to get the sports news. The problem is that the DataGrabber gets the data asynchronously and ends up hopping from several NSURLConnection delegate methods. How do know in FooClass when data has been received?
The delegate pattern used with a strict protocol is very useful for this (that's how DataGrabber would find out when NSURLConnection is done, right?). I have written a number of Web APIs that consume XML and JSON information this way.
// In my view controller
- (void) viewDidLoad
{
[super viewDidLoad];
DataGrabber *dataGrabber = [[DataGrabber alloc] init];
dataGrabber.delegate = self;
[dataGrabber getData:[NSDictionary dictionaryWithObjectsAndKeys:#"news", #"instruction", #"sport", #"section", nil]];
}
Then in your DataGrabber.h file:
#protocol DataGrabberDelegate
#required
- (void) dataGrabberFinished:(DataGrabber*)dataGrabber;
- (void) dataGrabber:(DataGrabber*)dataGrabber failedWithError:(NSError*)error;
#end
And in DataGrabber.m:
- (void) getData:(NSDictionary*)dict
{
// ... Some code to process "dict" here and create an NSURLRequest ...
NSURLConnection *connection = [NSURLConnection connectionWithRequest:req delegate:self];
}
- (void) connectionDidFinishLoading:(NSURLConnection*)connection
{
// ... Do any processing with the returned data ...
// Tell our view controller we are done
[self.delegate dataGrabberFinished:self];
}
Then make sure that Foo is implements the DataGrabberDelegate protocol methods to handle each case.
Finally, your DataGrabber has a delegate property (make sure you use assign, not retain to avoid retain cycles):
#property (nonatomic, assign) id<DataGrabberDelegate> delegate;
And when the NSURLConnection asynchronous loads are finished inside of DataGrabber, they call back to your UIViewController in the protocol laid out above so that you can update the UI. If it's ONE request, you could theoretically get rid of DataGrabber and put it inside your view controller, but I like to "separate my concerns" - API and View Controller stay separate. It generates an extra layer, but it keeps "text processing code" out of the view controllers (specifically for JSON and XML parsing code).
I've done this many times with success - one other key is that it's good to provide the user with some feedback that a page is loading - turn on the activity indicator in the status bar, show them a UIActivityIndicator, etc., and then when your delegate callback comes back with either success or failure, you get rid of it.
Finally, I've written a more detailed blog post about this: Consuming Web APIs on the iPhone
you could implement notifications for your DataGrabber class that go off any time you receive a certain amount of data (or when the download is finished if you want) and then the notified method (read about Notifications in the documentation) can do any handling you might want.
Note: it'd be helpful if FooClass was the delegate of DataGrabber
I also use notifications for this. Here is a good detailed explanation of how to set this up.
I had a data loader send a message to its delegate when its done
- (void) loadingMethod {
// Loading stuff...
[delegate LoaderDidFinish];
}
The delegate then relases the loader that called it, then does some stuff.
- (void) LoaderDidFinish {
[Loader release];
// Do stuff
}
This caused a bad acces error.
I assuemed this was cause because after LoaderDidFinish finished control returned to the loadingMethod which had been released.
However what suppried me was the fact that releasing the Loader later on in the method fixed the problem:
- (void) LoaderDidFinish {
// Do stuff
[Loader release]; // Now there is no bad access error!
}
Can someone explain why this worked?
It is just pure luck that you do not get any bad access errors by moving the call to release.
If you need to release the loader after you get the callback, it should either be deferred in the runloop, eg. by using [self performSelector:#selector(deferredLoaderDelete) withObject:nil afterDelay:0.0f] or by autoreleasing the loader in the callback.
Something in your // Do stuff uses some part of Loader.
In general, I'm not sure it's optimal to release an object from it's delegate. But if you must, an autorelease would have avoided this problem.