I'm using the JSON Framework addon for iPhone's Objective-C to catch a JSON object that's an array of Dictionary-style objects via HTTP. Here's my connectionDidFinishLoading function:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[loadingIndicator stopAnimating];
NSArray *responseArray = [responseString JSONValue]; // Grab the JSON array of dictionaries
NSLog(#"Response Array: %#", responseArray);
if ([responseArray respondsToSelector:#selector(count)]) {
NSLog(#"Returned %# items", [responseArray count]);
}
[responseArray release];
[responseString release];
}
The issue is that the code is throwing a EXC_BAD_ACCESS error on the second NSLog line. The EXC_BAD_ACCESS error I think indicates that the variable got released from memory, but the first NSLog command works just fine (and shows that the data is all there); it seems that only when calling the count message is causing the error, but the respondsToSelector call at least thinks that the responseArray should be able to respond to that message. When running with the debugger, it crashes on that second line, but the stack shows that the responseArray object is still defined, and has 12 objects in it (so the debugger at least is able to get an accurate count of the contents of that variable).
Is this a problem with the JSON framework's creation of that NSArray, or is there something wrong with my code?
count returns an NSUInteger, not an object. Per the Apple Documentation you should use %lu and cast to unsigned long in this case instead to display it:
NSLog(#"Returned %lu items", (unsigned long)[responseArray count]);
With %#, NSLog() tries to interpret the value as a pointer to an object and to send a description message to it.
It is highly unlikely that there is accidentally a valid object at that memory location, so the code is bound to fail badly or show unpredictable results.
In addition to what gf and Nikilai said, [responseArray release]; over releases
If those guys got their method naming right, NSArray *responseArray = [responseString JSONValue]; returns an autoreleased instance.
Simple typo: Your format string in the second NSLog contains an %# instead of %d.
Related
I'm not sure if this is a simulator issue, but I don't remeber having this problem before when I was using the iPad 5.0 simulator and below (now I'm running iPad 5.1 simulator). I overrode the description method for my Condition object to be:
- (NSString *)description {
NSString *str = [[NSString alloc] initWithFormat:#"Condition: %#", _conditionName];
return [str autorelease];
}
I have an array of these objects. My values are all valid. When I do:
for (Condition *p in self.reportsArray) {
NSLog(#"%#", [p description]);
}
It logs all my values, and then it crashes at the end. When I look at Instruments with Zombies, the last 4 calls are
-[NSPlaceHolderString initWithBytes:length:encoding:]
+[NSString stringWithUTF8String:]
-[NSAutoreleasePool release]
-[NSPlaceholderString initWithFormat:locale:arguments:]
Am I overriding description correctly?
Edit:
In Instruments, I get: message was sent to a deallocated object (zombie) at address:0x8ccf190. On the app itself, I get EXC_BAD_ACCESS.
It seems like the string returned from your description method is being released too soon.
Try rewriting your method using the stringWithFormat: class method.
- (NSString *)description {
return [NSString stringWithFormat:#"Condition: %#", _conditionName];
}
Make sure _conditionName is not a primitive.
Because the format string "%#" expects an object.
I'm a newbie when it comes to objective C, and am currently experiencing a memory leak with the following code snippet. The memory leak occurs with the 'responseObj' allocation. Whenever I try to release it similar to responseData, I get a crash.
-(void) connectionDidFinishLoading:(NSURLConnection *)connection
{
NSString* responseStr = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSMutableArray* responseObj = [responseStr objectFromJSONString];
[delegate loadGameDetails:[responseObj objectForKey:#"result"]];
[responseStr release];
[responseData release]; responseData = nil;
}
I also tried to autorelease as below, but I also get a crash:
[delegate loadGameDetails:[[responseObj objectForKey:#"result"] autorelease]];
What's the proper way for me to release this block of memory?
The problem is not in your allocation. Your are sending objectForKey: to a NSMutableArray which is actually a NSDictionary/NSMutableDictionary method. Double check what type of object objectFromJSONString really returns.
Nothing in that code is leaking. You have a possible over-release of responseData but I can't tell since the declaration and setup of it is not in scope. If it is an ivar you would typically set it to nil via the accessor (which would release it there) than directly as you have done here.
If there is a leak it is in your loadGameDetails method. What makes you think you have a memory leak? Have you run this through instruments, or used the static analyser?
I see a few similar questions, but no simple answers. I'm just playing around with NSMutableArray's to get a feel for them before I actually use them in my real project. For some reason, it's giving me an EXC_BAD_ACCESS error when I try to call count on the array, and I can't figure out why.
- (void) applicationDidFinishLaunching:(UIApplication*)application
{
// Create window and make key
_window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[_window makeKeyAndVisible];
NSMutableArray* test = [[NSMutableArray alloc] initWithObjects:[NSString stringWithFormat:#"first!"], [NSString stringWithFormat:#"second!"], nil];
[test insertObject:[NSString stringWithFormat:#"inserted"] atIndex:0];
NSLog(#"%#", [test objectAtIndex:0]);
NSLog(#"%#", [test objectAtIndex:1]);
NSLog(#"%#", [test objectAtIndex:2]);
NSLog(#"%#", [test count]); //bad access here
}
All the inserting and accessing EXCEPT the count method work just fine. I don't see why this isn't working, and would greatly appreciate some help. Thanks!
The %# format specifier prints objects. The return value of -count is just an unsigned integer. You should use the format specifier %u for that type.
The problem is that [test count] returns an NSUInteger not a pointer (to an NSObject). Try this instead:
NSLog(#"%u", [test count]);
Note that using %d also works, but %u is preferred.
- (NSUInteger)count; returns an NSUInteger.
Use this instead:
NSLog(#"%u", [test count]); //bad access here
count is working just fine. It does however return a NSUInteger primitive and not a pointer to a NSObject subclass. The %# string formatter expects a point to an object and logs the NSString returned from that object's -description method. When you pass it a NSUInteger NSLog assumes it to be an object pointer and dutifully tries to send a -description message to memory address 3 which causes that EXEC_BAD_ACCESS.
I'm writing a custom xml deserializer for an iphone app. As you can see below, I'm looping through all the list elements in the xml, I have debugged it, and with each loop there is a new and different element. The problem is that the xpath helper methods (there are similar ones to the one posted below, but for int and decimal) always returns the same value.
For example - 1st loop's xml "SomeValue" value will be "abc" and the helper method will return "abc", second item comes around and its xml "SomeValue" is "XYZ", but the helper method will still return "abc".
I'm new to iphone/objective c/memory managment so it could be any number of things. I just cant determine what the problem is :( could someone please offer some help?
-(void) deserializeAndCallback:(GDataXMLElement *)response
{
NSError * error;
NSArray *listings = [response nodesForXPath:#"//ListingSummary" error:&error];
NSMutableArray *deserializedListings = [[NSMutableArray alloc] init];
//loop through all listing elements, create a new listing object, set its values, and add
//it to the list of deserialized objects.
if(listings.count > 0)
{
for (GDataXMLElement *listingElement in listings)
{
Listing *list = [[Listing alloc] init];
//xpath helper function (shown below), just to get the value out of the xml
list.someValue = [QuickQuery getString:listingElement fromXPath:#"//SomeValue"];
[deserializedListings addObject:list];
}
}
if([super.delegate respondsToSelector: #selector(dataReady:)])
{
[super.delegate dataReady:deserializedListings];
}
}
+(NSString *) getString:(GDataXMLElement *)element fromXPath:(NSString *)xPath
{
NSError *error;
NSArray *result = [element nodesForXPath:xPath error:&error];
if(result.count > 0)
{
GDataXMLElement *singleValue = (GDataXMLElement *) [result objectAtIndex:0];
return singleValue.stringValue;
[singleValue release];
}
return nil;
[error release];
[result release];
}
EDIT: ok... I found a bit more info. Inside the helper function, the nodesForXpath method returns all the nodes from the entire xml, not just the current element I'm busy with. Does GDataXMLElement keep reference to its parent elements at all?
Example of what the xml looks like
<ListingSummary>
<SomeValue>abc</SomeValue>
</ListingSummary>
<ListingSummary>
<SomeValue>jhi</SomeValue>
</ListingSummary>
<ListingSummary>
<SomeValue>xyz</SomeValue>
</ListingSummary>
What you are seeing is correct behaviour for the XPath query you are using. You actually want a query relative to the current node, not the root of the document as you are doing.
See http://www.w3schools.com/xpath/
BTW + (NSString *)getString:(GDataXMLElement *)element fromXPath:(NSString *)xPath is a class method, not a static method.
You say that nodesForXPath: returns all the nodes from the whole document. Since you are calling that method with the same argument, #"//SomeValue", every loop, you get back the same array every time. This means that [result objectAtIndex:0] gives you the same object every time.
Also, as I mentioned in a comment, you should not be releasing singleValue, error, or result in your helper method. You don't own those and you're not responsible for their memory. On the other hand, since you create list using alloc, you do need to release it at the end of each loop; you are currently leaking a Listing object every pass.
It looks OK for me. Although the releases inside the getString:fromXPath: aren't necessary (you don't need to release parameters entered into a method or objects obtained from a NSArray. The proper way to release an object from a NSArray is removing it from the array, as for objects passed as a parameter, if you want to release it you should do it after you call the method.
The problem to your question must be somewhere else.
result.count should be [result count] since count is a method and not a property of NSArray
In my app i get a memory leak in
first
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
result = [[NSString alloc] initWithBytes:[webData mutableBytes]
length:[webData length]
encoding:NSUTF8StringEncoding];
[webData release];
}
second
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString * result = [[NSString alloc] initWithBytes:[webData mutableBytes]
length:[webData length]
encoding:NSUTF8StringEncoding];
[webData release];
}
in my first process I didn't get a memory leak(Declare a string object globally and I didn't release it)
In my second process i get a memory leak in string object.
Value stored to 'result' during its initialization is never read
Method returns an Objective-C object with a +1 retain count (owning reference)
Object allocated on line 124 and stored into 'result' is no longer referenced after this point and has a retain count of +1 (object leaked)
what is the difference?
In first case analyzer expects you to release your initted result string in, say, - (void)dealloc method of the class. If you don't - you'll get a leak as well (you'll see the leak if you run your app with via Instruments app with Leaks instrument added.
In second case you're creating a local variable in method scope where it should be released as well since you won't have a reference of it in any other method (i.e. if you try access result variable in other method you'll get unknown identifier error).
The difference is that the global String variable still points to the memory location containing the object, whereas the local variable goes out of the scope, so there is no pointer anymore and the memory has not been released.
That said, one has to understand that even situations technically not being leaks, could be seen as such, especially if one does not intend to use the global variable and just holds the reference for the lifetime of the application.