I have a Objective-C class that makes an HTTP request and, after receiving a response, calls methods on its delegate. The class provides some convenience methods around the built-in NSURLRequest for my application.
My question is: how do I ensure that I release this object? Normally, an object's owner is responsible for ensuring that an object gets released. In this case, there's no obvious owner. I want to create an instance of this class, set its delegate, fire off the HTTP request, and then forget about it (I could be making several of these requests, so the only way to give it an owner would be to keep a list of the request objects in the calling object, which seems suboptimal).
One approach I've considered: when the HTTP response comes in, my object calls its delegate and then does [self autorelease]. The problem is that all clients have to remember not to release this instance (or else my object needs to call retain before it returns to the user).
Anyone have any good patterns for this?
Apple have already thought of this pattern. On the assumption that you are using NSURLConnection, check out the documentation for -initWithRequest:delegate: Here is what it says about the delegate parameter:
The connection retains delegate. It releases delegate when the connection finishes loading, fails, or is canceled.
[my emphasis]
So when you create the connection, set your object as the delegate. It won't go away until the connection is terminated for whatever reason, even if no other object of yours retains ownership.
By the way, the statement
there's no obvious owner
is false. You say your object has a delegate. The delegate is the obvious candidate to be an owner.
Related
Sorry if this is a dumb question, but I'm debugging a dangling pointer issue in my App and I need to know - Is it possible for the app to resignActive without first enterBackground?
To becomeActive without first enterForeground?
My situation - I have class (called 'Connection') that manages all of the application's requests/connections. It needs to be available for as long as the app is alive, really.
In my -didFinishLaunchingWithOptions I init an instance of Connection and set some params. In my -applicationWillEnterBackground I suspend Connection's actions.
And, in my -applicationWillEnterForeground I check to see if the Connection instance still exists. If it does, simply resume actions, else a new one is created.
When I leave the app running for a while (an hour or so, with or without going back to the home screen), the app crashes with this error.
"2012-08-19 13:08:42.708 Picsilk[11108:707] *** -[Connection respondsToSelector:]: message sent to deallocated instance 0x24e120"
The class, 'Connection', is just a simple NSObject subclass, which manages an ASINetworkQueue instance and handles some specific needs I have for storing a user's tokens and authenticating requests to my api.
An instance of this class is created and managed by app delegate. My various view controllers use the app delegate to access the Connection, which automatically handles the queue (and non-queued requests as well)
Surely I'm missing something very simple and fundamental to my understanding of this SDK, please help. :]
The app can receive a UIApplicationWillResignActiveNotification when a call comes in or other notifications are presented. So, yes, it's possible "for the app to resignActive without first enterBackground".
I have a singleton wrapper class called "Folio APIWrapperv2" that is responsible for handling all of my API data calls within my iPhone app.
This class implements ASIHTTPRequest in order to fetch JSON from my server. When an ASIHTTPRequest has finished, it calls requestFinished:. In this method, I have additional processing of the data that can take a couple of seconds to complete. It's not major, but it slows down the app slightly, as it's a blocking call. To fix this, I create a new thread using GCD. However, when this thread runs, I get the following error:
[FolioAPIWrapperv2 respondsToSelector:]: message sent to deallocated instance 0x245050
This function is being called in ASIHTTPRequest's "handleBytesAvailable" method at the line:
if ([[self delegate] respondsToSelector:[self didReceiveDataSelector]]) {
I'm not sure what's going on here. FolioAPIWrapperv2 is a singleton class, so it should never be deallocated. I've tried searching online to see what other issues people have had with threading and couldn't find a suitable solution. Does anyone have an ideas?
Are you setting the delegate to be an instance of the class or the class itself? The delegate must be an instance of a class.
Otherwise, if you're getting the output:
[FolioAPIWrapperv2 respondsToSelector:]: message sent to deallocated instance 0x245050
Then it seems likely that your class that should never be deallocated is getting deallocated.
Either way, at least one way to approach this is to add some debugging, eg, when you create the request:
NSLog("set delegate to %p", request.delegate);
and similar logging for self in the init/dealloc of your singleton.
In my iPhone app, I've been able to use NSURLConnection properly to download data from a URL. I simply set the delegate to my UIView, and make sure that I set up the UIView to answer for the proper delegate functions such as -connection:didReceiveResponse:. However, if I have a number of NSURLConnections (either for a similar type of request, or multiple kinds of requests), it gets messy because the delegate functions, such as didReceiveRequest, don't differentiate between the different requests. The advantage of asynchronous requests is that you are supposed to be able to multiple at once, without blocking the main thread. What's the best practice for how to use multiple NSURLConnection requests at the same time?
I prefer to wrap them in a higher-level object (like ImageDownloader or SomeWebServiceCall) that has all the per-connection/call state information. I usually create a delegate these objects so that the caller gets a more specific callback when the operation has succeeded or failed.
Perhaps look into ASIHTTPRequest, instead of NSURLConnection. ASIHTTPRequest makes a lot of this work trivially easy.
In this case, I'd say NSOperation is your best bet. ASIHTTPRequest is based on NSOperation and allows you to handle each request as an operation, which serves as the delegate for its own NSURLConnection.
You'll have to be careful here though, because by default NSOperations are run on separate threads, but some APIs, like this one, are required to be executed on the main thread. If you inspect the source code to ASIHTTPRequest you'll notice they've got some infrastructure to ensure delegate methods are called on the main thread.
Create an instance variable for each NSURLConnection and NSMutableData. All of your delegate methods have the NSURLConnection object as an argument, so you can match them as such:
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (connection == aConnection) {
// Do something with the data for aConnection
} else if (connection == otherConnection) {
// Do something with the data for otherConnection
}
}
This still limits you to one connection per instance variable, so it's not suitable for, for instance, a table view with images on each row that need to be loaded.
I ran into the situation where my class is making one asynchronous web call and the user requests another one (by moving a map) and can't seem to properly cancel the NSURLConnection. I have seen some solutions to manage multiple async calls, but I do not want to do that because once the second request is made I no longer care about the first. I see that there is a cancel method, but I tried only calling it when the connection is nil, but the connection never seems to become nil despite a release on that object. Does anyone have a code sample of a proper cancel of an async NSURLConnect?
I don't think you understand Objective C message passing or retain/release. Sending a retain or a release to an object will never cause the pointer you are holding for that object to become nil (though the object may no longer be valid). In other words:
id a = b;
[a release];
//since release never changes the value of a
assert(a == b);
Okay, so having said that, if somehow a release code change the value of object pointer you called it against to nil, then sending a another message to it would do nothing. Messages to nil are silently dropped. Remember, what you are holding is a pointer to the object, not the object itself.
Having said that, if you want to cancel an NSURLConnection. You have to send it the cancel before you release it (in general it is never correct to send message to objects after you have released them).
I really recommend you read Apple's Memory Management documentation.
I would like to my object to receive notification with the DeviceToken becomes available. Is there a way to delegate only certain functions from the UIApplication to my class?
Update:
I'm not so much interested in accessing it from the application delegate, I already have an application delegate, but want to respond to the event via call back or some observer method if it's possible.
Despite not being marked as such in the documentation, all UIApplicationDelegate methods are optional, so you can simply implement the ones you require, and ignore the ones you don't.
However, you'll probably want to implement all three of the APS methods, rather than just the one indicating registration success!