Using custom subclass of NSURLConnection, how does it "find" the additional data in the class later? - objective-c

This blog offers a nice solution for handling multiple NSURLConnections: make a custom "CustomURLConnection" class that has an additional tag property.
http://blog.emmerinc.be/index.php/2009/03/02/custom-nsurlconnection-class-with-tag/
http://blog.emmerinc.be/index.php/2009/03/15/multiple-async-nsurlconnections-example/
Basically, he has simply added a tag property to the exsisting NSURLConnection:
CustomURLConnection.m
- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately tag:(NSString*)tag {
self = [super initWithRequest:request delegate:delegate startImmediately:startImmediately];
if (self) {
self.tag = tag;
}
return self;
}
then, later in the normal NSURLConnection loading methods, you can do:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
//Log the connection’s tag
CustomURLConnection *ttttag = (CustomURLConnection *)connection; // **HERE**
NSLog(#”%#”, ttttag.tag);
NSMutableData *dataForConnection = [self dataForConnection:(CustomURLConnection*)connection];
[connection release];
}
So, that's where I'm having trouble. The way I see it, this is how things go:
I create a "connection+tag"
The first code snippet I posted above creates a regular "connection" (no tag), which will eventually call the the normal NSURLConnection methods like connectionDidFinishLoading. What happens to the tag at this point?
In the connectionDidFinishLoading method I'm able to cast the connection back into a "connection+tag", then find that missing tag information that had been discarded. How?
Maybe I'm just confusing myself, but it seems as if the tag was discarded when it starts down the normal NSURLConnection path. But then by casting it as the subclass, I'm again able to recover the tag property. Where did it live/go in the mean time?
Could someone with a better understanding of inheritance explain this to me?

With this code:
[[CustomURLConnection alloc] initWithRequest:... delegate:... startImmediately:... startImmediately tag:...];
you create an instance of CustomURLConnection. Now here is where your understanding is wrong: this CustomURLConnection object can freely call all methods of its superclasses but it will always remain a CustomURLConnection. The tag is always there.
The methods that are defined in the superclass such as initWithRequest:delegate:startImmediately: don't know about the tag but they don't have to, either. When the delegate method gets called:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
the connection argument is the very same CustomURLConnection that you created yourself above. The type in the method signature is different but that doesn't matter; because you know that this connection is of the CustomURLConnection type, you can just cast the connection object to the correct type and access the new property. But even if you wouldn't do that, the tag would still be there all the time.

I'm not sure what you mean by:
The first code snippet I posted above creates a regular "connection" (no tag).
What you've done here is create a subclass of NSURLConnection. Anywhere you can use the latter, you can use the former. NSURLConnection* means "a pointer to an NSURLConnection* or a subclass of it." So the original object you created was a CustomURLConnection and it included an extra ivar. That ivar doesn't disappear just because intermediary users refer to it by its superclass.

Related

Objective C Unsure where method is being called?

Showing what a novice I am with Objective C here. The second of these two methods is getting called by the method above. Though I have absolutely no idea where? I want to be able to wrap the part that calls the second method in an if statement to determine if the file did exist based on the returned Boolean. Example code would be appreciated, if anyone could also explain how this second method gets called that would also be fantastic.
-(void) queryResponseForURL:(NSURL *)inURL {
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:inURL];
[request setHTTPMethod:#"HEAD"];
NSURLConnection * connection = [NSURLConnection connectionWithRequest:request delegate:self];
// connection starts automatically
}
-(BOOL)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
if([(NSHTTPURLResponse *)response statusCode] == 200){
NSLog(#"file exists");
return YES;
}else return NO;
}
It is getting called by the NSURLConnection object, so you can't intervene in the code that calls it. But you shouldn't need to - the purpose of a delegate method is to enable you to hook in to that inaccessible code.
The method signature is this, by the way:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
It doesn't have a return value and you can't change the method signature, as it's part of the NSURLConnection object's protocol. Remember, you don't call this method, the connection object does. But you can respond to the method. Instead of attempting to return a BOOL, you can deal with your 'if file..' code inside this method, or call another method from it. You should bear in mind that this is triggered (asynchronously) when a response is confirmed to have been initiated - so you can check HTTP header info such as mime type - but it doesn't guarantee a completed data transfer (there are other delegate methods for that).
See also the developer guide to NSURLConnection
The second method is a delegate or callback method. The callback method is called when your connection instance receives a response from the server your connecting with. NSURLConnection has a few informal protocols on NSObject. This means that methods defined in these protocols will automatically be called when any subclass of NSObject has implemented these methods. Your subclass of NSObject has implemented one of these callback methods, therefore this method is called. Most delegates are declared as formal protocols and need to be explicitly defined in your classes in order to make use of them. The informal protocols of NSURLConnection are exception to the rule, though this is about to change in future versions of iOS.
P.S.: a protocol in Objective-C is essentially the same as an interface in Java / C#.
More info here: http://developer.apple.com/library/ios/ipad/#documentation/Foundation/Reference/NSURLConnectionDataDelegate_protocol/Reference/Reference.html
I guess you want to cancel the connection when you receive the wrong response? What you should do is the following:
Store your URLConnection in an ivar or property (personally I'd prefer a property).
In your -connection:didReceiveResponse delegate method, if the response status code isn't an appropriate value, cancel your connection. You can use your ivar or property for this purpose.

How to convert a delegate-based callback system into block-based?

I have a class, which has a delegate based system for sending different type of requests. it uses delegate to tell the object when the request is complete and also if it was a success o an error.
Now, I also have to check what type of request was it in response to take appropriate action.
I have wrapper class that should give me a block based interface for the same.
I pass a completion-block and an error-block to a request method which should internally use this delegate based class.
And when the response comes, should automatically call the appropriate handler for that request type and depending on success and error as well.
I saw a similar question on SO, but it was a little unclear to me, So please give a general idea of how to go about it instead of marking it as duplicate straight away.
Here is one way to do it. Use this RAExpendable class to dynamically build a delegate with a block based implementation.
Let's say your delegate is:
#protocol XDelegate
-(void) foo:(id)response;
#end
Add RAExpendable.h, RAExpendable.m from https://github.com/evadne/RAExpendable to your project. Dynamically add the delegate method:
RAExpendable *expendable = [RAExpendable new];
[expendable addMethodForSelector:#selector(foo:) types:"v#:#" block:^(id x, SEL sel, id response){
NSLog(#"response is %#", response);
}];
And set the expendable class as your delegate:
someObject.delegate = expendable;
Now, if you do this:
[expendable performSelector:#selector(foo:) withObject:#"OK"];
You get the string response is OK. Replace NSLog with whatever success/failure implementation you see fit. From now on, when you call foo:, the block executes instead.
If you want to modify this for your use case, note that the parameters for this example were v#:#, which according to the Type Encoding guide of the runtime means: void return, self, SEL, object. self and SEL are the two hidden parameters present on every Objective-C methods, the third parameter is the first non hidden parameter of the method. The signature of the block has to match the signature of the method.
With REKit, you can make a delegate dynamically like below:
id dynamicDelegate;
dynamicDelegate = [[NSObject alloc] init];
[dynamicDelegate respondsToSelector:#selector(foo:) withKey:nil usingBlock:^(id receiver, id response) {
NSLog(#"response is %#", response);
}];
someObject.delegate = dynamicDelegate;

Objective-C: How can I release this object?

I have a class named "ServerDataLayer" that holds a NSURLConnection, and a NSMutableData that its writing the received HTTP data in to. When the connection finishes, it simply fires a delegate that my caller passed itself as a reference, the method looks like this:
-(void) serverDataLayerResponse:(id)entity
{
if ([entity isMemberOfClass:[LoginResponse class]])
{
LoginResponse *response = (LoginResponse*)entity;
NSLog(#"Error Code: %d", response.errorCode);
NSLog(#"Error Message: %#", response.errorMessage);
NSLog(#"Registered: %c", response.registered);
NSLog(#"AuthToken: %#", response.authToken);
[AppData shared].authToken = response.authToken;
ServerDataLayer *request = [[[ServerDataLayer alloc] initWithServer:_serverUrl delegate:self] autorelease];
[request getPlayerDetails];
//[_server getPlayerDetails];
}
}
Here's my problem...the internal _receivedData and _connection variables are currently in use whilst this delegate method is in progress. I wanted to use my same ServerDataLayer instance to fire another request off "[_server getPlayerDetails]", but the _connection and _receivedData variables internally were getting overwritten and I was getting in to a mess about when to retain/release at the right time.
So my work around was just to instantiate the ServerDataLayer each time I wanted to talk to the server. Now...in the example above, I'm instantiating the request with an 'alloc', and setting an 'autorelease' as I lose scope of this 2nd request. Will this 2nd request stay in memory whilst it's NSURLConnection is busy internally performing the request?
I'm getting a bit lost at this point on how to manage the object references for this kind of process. Any help would be appreciated.
An NSURLConnection, if used via the delegate methods will attach itself as an input to a run loop. However it won't retain its delegate. So your ServerDataLayer would be deallocated (and hopefully remember to cancel the connection). You could use object associations to give your object the same lifecycle as the URL connection, if you were suitably careful about the potential retain loop.
If you use sendAsynchronousRequest:... then you'll probably be fine anyway; assuming you reference self or any instance variable in the completion block then you'll be retained by the block and live for at least as long as it does.
There's really no need to confuse yourself over retain/release any more. Even if ARC isn't an option, you can just declare the relevant instance variables as retain properties within a class extension and use self.property notation to set new values. Retains and releases will be handled for you.
The only caveat is that you should never use dot notation in either your init or dealloc as a special case of the rule that it isn't safe to call methods on a class that's only half instantiated or is half destroyed.
Just use multiple ServerDataLayer instances.

ARC: Getting EXC_BAD_ACCESS from inside block used in delegate method

I must be doing something wrong, but the Automatic Reference Counting docs don't give me a hint on what it might be. What I'm doing is calling a method with a block callback from inside a delegate method. Accessing that same delegate from inside the block results in a bad access. The problem is the object I'm passing - loginController which is sending the message to its delegate - is clearly not released, when I don't access it inside the block I can call the method multiple times without an issue. Here's my code:
- (void)loginViewDidSubmit:(MyLoginViewController *)loginController
{
NSString *user = loginController.usernameLabel.text;
NSString *pass = loginController.passwordLabel.text;
__block MyLoginViewController *theController = loginController;
[self loginUser:user withPassword:pass callback:^(NSString *errorMessage) {
DLog(#"error: %#", errorMessage);
DLog(#"View Controller: %#", theController); // omit this: all good
theController = nil;
}];
}
NSZombieEnabled does not log anything and there is no usable stack trace from gdb. What am I doing wrong here? Thanks for any pointers!
Edit:
I figured the problem has a bigger scope - the callback above is called from an NSURLConnectionDelegate method (the block itself is a strong property for that delegate so ARC should call Block_copy()). Do I need to take special measurements in this scenario?
Flow (the loginController stays visible all the time):
loginController
[delegate loginViewDidSubmit:self];
View Delegate
(method shown above calls the loginUser: method, which does something like:)
httpDelegate.currentCallback = callback;
httpDelegate.currentConnection = // linebreak for readability
[[NSURLConnection alloc] initWithRequest:req
delegate:httpDelegate
startImmediately:YES];
NSURLConnectionDelegate
- (void)connection:(NSURLConnection *)aConnection
didFailWithError:(NSError *)error
{
if (NULL != currentCallback) {
currentCallback([error localizedDescription]);
self.currentCallback = NULL;
}
}
And this is where I get the bad access, but ONLY if I access that loginController variable...
Set copy attribute to the property, or just call 'copy' method for the block.
- (void)loginUser:(NSString *)user withPassword:(NSString *)pass callback:(void (^callback)(NSString *))
{
callback = [callback copy];
The actual solution was that I had the block as a strong property, but it should have been a copy property! D'oh!
First "Solution":
I just found a way to prevent the bad access. As shown in my Edit above, the View Delegate forwards the block to the httpDelegate (an instance of another class), which in turn keeps a strong reference to the block. Assigning the block to a temporary variable and forwarding the temporary block variable solves the problem, for whatever reason. So:
This crashes on block execution, as described
httpDelegate.currentCallback = callback;
This works
MyCallbackType aCallback = callback;
httpDelegate.currentCallback = aCallback;
I'll accept this as the answer, if anybody has more insights I'm happy to revise my decision. :)
I figure what is happening there is that the loginController is dead right after calling its delegate. Therefore a crash occurs. Without more information I can think of possible scenarios only:
The block do not retains the loginController object (__block type modifier). If the block is executed asynchronously, the loginController might no longer be available if it was killed elsewere. Therefore, no matter what you want to do with it, you wont be able to access it inside the block and the app will crash. This could happen if the controller is killed after sending loginViewDidSubmit.
I think most likely this could be your situation: The loginController calls its delegate object. The delegate method ends up synchronously invoking the callback block that kills the controller. The controller is expected to be alive after invoking the delegate method. Killing it inside the delegate method, most likely will cause crashes to happen. To make sure this is the problem, simply nil the loginController in the delegate method and put an NSLog statement in the controller after calling the delegate, never mind the block, you will get a crash there.
Perhaps if you paste some code we could help more.
My best.

Making non-self delegate in obj-c

In order to use asynchronous http requests in objective c, you need to set a delegate to NSURLConnection. The problem is that I need to make multiple http requests, so having the same delegate (self) wont work.
What is the best way to go about this? Should I make a new delegate class for each http request? Are these delegates just NSObjects?
You have a few options. The two most most common are:
Make a new class for each connection (yes, a subclass of NSObject) and set them as delegates -- have them carry out whatever logic you need when the data is loaded
Set one class as the delegate and store references to all of your NSURLConnections. That way, when your delegate gets - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data called, you can test which NSURLConnection is being used (eg if ([connection == myConnection]) -- or whichever delegate method you're implementing)
What I do is make a class that will handle downloading a file, and notify me when it is done through a selector. I pass it a delegate, a selector and the Info it needs to perform the download.
- (void) downloadFileFrom:(NSString*) httpLocation respondAt:(SEL)selector on:(id)target withParam:(id)param
{
self.finishSelector = selector;
self.delegate = target;
self.responseParams = param;
}
the Class is its own NSURLConnection delegate. Therefore the instance is separated from the others that I may instantiate, And it handles creating its own result for me to work with. I hold onto the param object. which could be anything.
At the end of the download it does a performSelector: on the delegate. passing itself to the delegate.
if ([self.target respondsToSelector:self.selector])
{
[self.target performSelector:self.selector withObject:self.param];
}
then you can create an instance of the downloader and call your method... telling it where to reply to you.
MyDownloader downloader = [[MyDownloader alloc] init];
[downloader downloadFileFrom:#"http://www.mydomain.com/myimage" respondAt:#selector(myFileIsComplete:) on:self withParam: downloader];
[downloader autorelease];
another option is to create a #protocol for your class to respond at, and have you delegate conform to the responder.
That should work, but there is another option to consider. You could make a generic class that creates and calls the NSURLConnection provided they are common enough. Then keep an NSArray or NSDictionary of the classes. One for each connection.
Example: I have an app that needs to download several photos simultaneously. Therefore, I have a GetFlickrPhoto class. It has a custom init method that receives the URL and any other necessary info. Each individual class creates the NSURLConnection and can safely set the delegate to self
This helps keep things contained and very manageable/reusable.
To take it a step further:
The app I mentioned before, also needed to download JSON feeds. So I made a GenericDownload class that took in URL and asynchronously downloaded the NSData and then returned the NSData to the calling delegate via defined success/failure protocols. It didn't care what the NSData contained.
I remodeled GetFlickrPhoto to call GenericDownload and use the returned NSData for a photo. I then made a GetJSON class that also called GenericDownload and parsed the returned NSData into a JSON feed.
Takes a bit more time but in the end you will be glad for maintenance and future projects.