Hey, I'm a beginner in Objective C, and my .NET and Java years have rusted my memory management skills, so it's very likely I'm missing something here.
I am building an iPad app. The main view is a SplitView with a TableView on the left, and the detail view contains another TableView. The loading of the latter with data has been commented out in an attempt to single out my problem.
The app seems to work fine (has to fetch data from a .NET WS and parse it into the table), but at random times I receive a BAD_ACCESS or a "selector not recognized" errors.
The selector not recognized error I get here:
-(void) connection:(NSURLConnection *) connection
didReceiveResponse:(NSURLResponse *) response {
[webData setLength: 0];
}
This piece of code I'm sure most of you know about, I got it from all samples I found online and in books to send a web request.
Beats me why it says it does not recognize the setLength selector, webData is defined as
NSMutableData *webData;
Any ideas?
Thanks.
If you do not allocate your webData object either with
NSMutableData* webData = [[NSMutableData alloc] initWithCapacity:2048];
or
NSMutableData* webData = [[NSMutableData data] retain];
then the webData object will most likely be autoreleased during one of the context switches from the NSURLConnection message that you allocated it in (probably connection:didReceiveData:) to the connection:didReceiveResponse: message.
Any object that you do not alloc or explicitly retain is likely to be deallocated during scope changes, even if it is a member variable of your class.
Most likely you aren't creating the NSMutableData correctly. I expect you have code that looks like
webData = [NSMutableData data];
This is going to give you an autoreleased object, and yet you're storing it in an ivar. You need to take ownership of the object when storing it in an ivar. In your case, the simplest way is just to skip the convenience method and go with alloc/init:
webData = [[NSMutableData alloc] init];
For more details, read the Memory Management Programming Guide.
Seems to be very usual (not only beginners) error, when connection is not cancels in dealloc or viewWillDisappear. When you are leaving the controller, you should cancel all connection, timers, etc, created by the controller, to prevent them from calling delegate methods or selectors on deallocated controller objects.
It looks like webData is being deallocated and replaced with some other object. Make sure you retain it if you don't use alloc/init or mutableCopy to get it.
Related
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.
I'm needing to quickly and simply parse an XML file in my app. The best solution for me seems to be something along the lines of an XML-to-NSDictionary converter which I can then dig through easier.
I chose to use this small class: http://troybrant.net/blog/2010/09/simple-xml-to-nsdictionary-converter/
However I'm confused about the returned NSDictionary* and its state in memory. The class method dictionaryForXMLData allocs and inits the class. objectWithData then goes off and does the heavy lifting returning the NSDictionary object which gets returned to the caller.
The confusing part for me is [reader release] before the return (I understand this must happen because of the way reader was created). My understanding of NSDictionaryis that all the objects will also be sent the release message which means the object that we're returning shouldn't have a retain count. Note that the object is created with a convenience method and only gets a retain count when it is added to the stack.
Should the return statement not be return [rootDictionary autorelease]. The problem I see with that is that there is no telling when rootDictionary will be released and depending on the size of the XML file it could sit there clogging memory? Or do I have it all wrong?
What they are doing is not correct. The object returned by objectWithData: is possibly simply a reference to something retained inside reader. And by releasing reader, you are potentially deallocating the rootDictionary you got earlier. Depending on the inner workings of the XMLReader class, it is possible that the thing they return from objectWithData: is retained and autoreleased; but that is an implementation detail, and you cannot depend on it (plus Cocoa memory management is local, so you should never care about what other functions do).
The correct thing to do would be to retain the rootDictionary you get, and then autorelease it (to balance the retain since you have to return it without ownership).
+ (NSDictionary *)dictionaryForXMLData:(NSData *)data error:(NSError **)error
{
XMLReader *reader = [[XMLReader alloc] initWithError:error];
NSDictionary *rootDictionary = [[[reader objectWithData:data] retain] autorelease];
[reader release];
return rootDictionary;
}
I ran into this problem while trying to fix a memory leak with the facebook-ios-sdk. How do i handle this situation when passing objects from no arc compiled classe to arc enabled classe?
This is the code inside the non arc compiled Facebook library: (i removed the unnecessary stuff which is not related to the problem) as you can see, result object is not autoreleased or released.
- (void)handleResponseData:(NSData *)data {
NSError* error = nil;
id result = [self parseJsonResponse:data error:&error];
self.error = error;
// Call the defined delegate wich is my AppDelegate didLoad method wich is arc enabled
[_delegate request:self didLoad:result];
}
- (id)parseJsonResponse:(NSData *)data error:(NSError **)error {
SBJSON *jsonParser = [[SBJSON alloc] init];
//gets the object wich leaks or gets overreleased
id result = [jsonParser objectWithString:responseString];
[jsonParser release];
return result;
}
Now if i try to add autorelease to the result object, i am facing a NSZombie when my arc code in my AppDelegate try's to release the object. However if i leave the code like this i'm facing memory leaks whit the result object which gets not released.
am i missing something basic? i can't get my head around this?
Thanx for any advice! Chris
The result returned from -parseJsonResponse:... is autoreleased already (see note at bottom).
Since the name of the -parseJson method doesn't begin with new, alloc, copy or mutableCopy, the compiler assumes that it returns an object with a +0 reference count, meaning it needs to be retained by the calling code if it is to be kept around, and doesn't need to be released if it's not being kept around. That's a long winded way of saying that it should neither leak nor cause a crash in your ARC code as written in your question.
Passing objects between ARC code and manual reference counting code doesn't require any special handling. You just need to make sure that methods' names match their memory management semantics in the non-ARC code. It certainly seems like you've done that in this case, although as you say, you didn't post your complete code.
Note: Presumably, objectWithString: returns an autoreleased object. If it doesn't it, it should (because it doesn't start with alloc, new, copy, mutableCopy).
I'm using the iPhone SDK 3.0, but I think this is a general misunderstanding of how things work w/ c & memory management.
I've overridden the viewWillAppear method like this
#implementation MyViewController
- (void)viewWillAppear:(BOOL)animated {
NSArray *items = [NSArray arrayWithOjbects:self.searchButton, self.trashCan, nil];
[self.bottomBar setItems:items animated:YES];
}
// other stuff...
#end
when I try to switch away from the view controller above and switch back everything works properly.
BUT, my inclination is to "release" the original pointer to "items" because I think a reference to the NSArray is now held by bottomBar.
But when I do this (see code below) and try to switch away from the UIViewController, I get a memory management error (-[CFArray count]: message sent to deallocated instance 0xd5f530).
- (void)viewWillAppear:(BOOL)animated {
NSArray *items = [NSArray arrayWithOjbects:self.searchButton, self.trashCan, nil];
[self.bottomBar setItems:items animated:YES];
[items release];
}
Do I need to not release items in this case? Or am I doing something wrong?
Obviously, the empirical evidence indicates that I shouldn't release "items", but it's not clear to me why this is the case.
Thanks for any info/"pointers"!
You do not need to release it because you never init'd it. [NSArray arrayWithObjects:...] returns an autoreleased object. You are not responsible to release it, because it has had the autorelease message sent to it when it returned from the method. You only have to release what you init! (If you had used [[NSArray alloc] initWithObjects:...] you would have had to.)
When you call arrayWithObjects: on NSArray:
NSArray *items = [NSArray arrayWithObjects:self.searchButton, self.trashCan, nil];
You are returned an autoreleased array. The array is returned to you autoreleased, because you do not call alloc, new, or a method containing copy on it. This signifies that you do not need to memory manage that object. (Take a look at the Memory Management Programming Guide for Cocoa for more information)
However, it is then retained when you call setItems on self.bottomBar, passing the array as an argument, bumping its retain count up to 1, but then you release it, returning its retain count back to zero, which causes it to be deallocated.
Since the array is retained by self.bottomBar, this implies that it is managing the memory of the array. When it is no longer needed, the array will be released, implying that the class no longer needs the array, which is the correct way to manage the memory.
For heavens sake guys, just point people to the Memory Management Rules. Don't paraphrase them. Don't say "returns an autoreleased object" (which is not necessarily true, and is irrelevent even when it is true). Just point them to the rules.
The rules are a sum total of 9 paragraphs! There is no need to paraphrase them, abrieviate them, or restate them. They are clear and concise and explicit.
Read the rules, follow the rules, and you will have no memory management problems.
Here's the short version:
+[NSArray arrayWithObjects:] returns an object that you do not own, so no, you should not release it.
On the other hand, if you had done:
NSArray *items = [[NSArray alloc] initWithObjects:self.searchButton, self.trashCan, nil];
this creates an object with a retain count of 1, so you would need to release it to prevent it from leaking.
Check out the Memory Management Programming Guide for Cocoa for more details.
Summary of my question: Does NSURLConnection retain its delegate?
Detailed question and scenario:
I have a custom class, called JsonDownloader that is takes in a URL and returns an NSDictionary of the JSON that the URL returns.
On an iPhone app, I do something like this. (the init method kicks off the whole process)
- (void)viewDidLoad {
JsonDownloder *temp = [[[JsonDownloader alloc] initWithURL:urlString returnDataTo:self]];
[temp release];
[super viewDidLoad];
}
When the JsonDownloader is done downloading and parsing, it performs a callback to the returnDataTo: object, in this case, the calling object.
This works just fine. Even if I introduce a 30 second delay in my web servers response, the JsonDownloader still exists and does it's callback correctly.
So my questions is this: What is keeping JsonDownloader way past the end of the event cycle? I am explicitly releasing it.
My hunch is that NSURLConnection must do a retain on its delegate, but I didn't see anything in the documentation. Anyone have an ideas?
There aren't many setters that don't either copy or retain a variable being passed to it, lest the memory of said variable be re-allocated to something else when its retain count reaches zero.
However, the answer is YES, it does. A little bit of test code shows the delegate's retain count go up:
NSLog(#"Retain count before: %d", [self retainCount]);
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://google.com"]];
NSURLConnection* conn = [NSURLConnection connectionWithRequest:request delegate:self];
NSLog(#"Retain count after: %d", [self retainCount]);
which produces in the log:
Running…
2009-07-09 02:13:40.516 delegateRetain[45123:a0f] Retain count before: 1
2009-07-09 02:13:40.525 delegateRetain[45123:a0f] Retain count after: 2
Debugger stopped.
So you can see pretty clearly that in connectionWithRequest:delegate: "self" is indeed having its retain count increased +1. If you're feeling brave and want to mess with the EXC_BAD_ACCESS gods, add in
[conn dealloc];
NSLog(#"Retain count after dealloc: %d", [self retainCount]);
which will print out "1" again, showing a post dealloc decrement. However, you'll get a nice Program received signal: “EXC_BAD_ACCESS”. because the NSAutoreleasePool will try to release the connection and it will be gone ;)
Most delegate properties are not retained but assigned to prevent circular references. See this question about that as well.
However, NSUrlConnection does not have a specific delegate property. You have to specify the delegate along with the initialization of the connection. I think that is why it does receive a retain, as Dave Martorana showed.
Yes, "The connection retains delegate. It releases delegate when the connection finishes loading, fails, or is canceled," according to the Xcode Documentation for -[NSURLConnection initWithRequest:delegate:] under Special Considerations. See also: NSURLConnection inherent memory leak?