iPhone - Objective-C Memory Leak with SBJsonParser - iphone

I keep getting the following memory leak using the "Leaks" tool in Xcode. As this is a library, I'm just wondering what would be the best way to fix such a leak. Any help would be greatly appreciated. I am happy to share more code if needed.
UPDATE: I found this article, which doesn't seem promising. Has anyone got any suggestions as to how to fix this?
http://code.google.com/p/json-framework/issues/detail?id=13
This is how I'm using the library.
- (void)getFacebookProfileFinished:(ASIHTTPRequest *)request {
NSString *responseString = [request responseString];
NSMutableDictionary *responseJSON = [responseString JSONValue]; //memory leak 100%
NSString *username;
NSString *firstName = [responseJSON objectForKey:#"first_name"];
NSString *lastName = [responseJSON objectForKey:#"last_name"];
NSString *facebookId = [responseJSON objectForKey:#"id"];
if (firstName && lastName) {
username = [NSString stringWithFormat:#"%# %#", firstName, lastName];
} else {
username = #"";
}
UIAppDelegate.userSessionId = facebookId;
UIAppDelegate.userFullName = username;
if (UIAppDelegate.userSessionId != nil) {
Service1 *service = [[Service1 alloc] init];
[service UserExists:self action:#selector(handlerUserExists:) facebookUserId:UIAppDelegate.userSessionId];
[service release];
} else {
[Utility displayAlertMessage:#"There has been an error. Please try again later." withTitle:#"Error"];
[self logoutCompletely];
}
}

By commenting out the body of your if (line 50) you've made your release (line 51) conditional. Comment out the if (line 49) as well.
However, having said that your previous method has the same issue but apparently no warning, or maybe it was never used?

As CRD said above. You have the same leak in your JSONFragmentValue. Here is a correct non leaking version.
- (id) JSONFragmentValue
{
SBJasonParser *jsonParser = [SBJasonParser new];
id repr = [jsonParser fragmentWithString:self];
if (repr == nil)
{
NSLog(#"JSonFragmentValue failed:%#", [jsonParser ErrorTrace]);
}
[jsonparser release], jsonParser = nil;
return repr;
}
Or if you prefer autorelease pools.
- (id) JSONFragmentValue
{
SBJasonParser *jsonParser = [SBJasonParser new] autorelease];
id repr = [jsonParser fragmentWithString:self];
if (repr == nil)
{
NSLog(#"JSonFragmentValue failed:%#", [jsonParser ErrorTrace]);
}
return repr;
}

Related

String not getting split giving unrecognized selector error

Trying to split the string in to array, but it is giving error "[__NSArrayI componentsSeparatedByString:]: unrecognized selector sent to instance 0x11741b20'". The string contains the value, that comes from first index of array then the string needs to be split and store in array.
This is array value.
mcommarr:(
":comment",
":comment",
":comment"
NSString *strr = [[NSString alloc]init];
strr = [self.mCommArr objectAtIndex:indexVal];
NSArray *arr2 = [str componentsSeparatedByString:#","];
Here is the complete method in which i am using this.
-(void)loadData:(int)indexVal;
{
indexVal=serialIndexVal;
serialIndexVal++;
NSLog(#"arrLike:%d", [self.mArrLike count]);
NSLog(#"arrPid:%d", [self.mArrPid count]);
status = [NSString stringWithFormat:#"get"];
[self.mButtonsStatusDict setObject:status forKey:#"status"];
[self.mButtonsPidDict setObject:[self.mArrPid objectAtIndex:indexVal] forKey:#"pid"];
[self.activityIndicator startAnimating];
#try
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSString *status = [NSString stringWithFormat:#"get"];
[self.mButtonsStatusDict setObject:status forKey:#"status"];
[self.mButtonsPidDict setObject:[self.mArrPid objectAtIndex:indexVal] forKey:#"pid"];
self.mButtonsCommentsDict = [MyEventApi showComments:self.mButtonsPidDict];
self.mButtonsDict = [MyEventApi likeDislike:self.mButtonsUidDict post:self.mButtonsPidDict postStatus:self.mButtonsStatusDict];
dispatch_sync(dispatch_get_main_queue(), ^{
[self.activityIndicator stopAnimating];
NSLog(#"buttons data dict:%#", self.mButtonsDict);
if([self.mButtonsDict count] == 0)
{
NSLog(#"server problem no response");
[self.mArrLike addObject: #"0"];
[self.mArrDislike addObject: #"0"];
}else{
[self.mArrLike addObject: [self.mButtonsDict valueForKey:#"like"]];
[self.mArrDislike addObject: [self.mButtonsDict valueForKey:#"dislike"]];
}
if([self.mButtonsCommentsDict count] == 0)
{
NSLog(#"server problem no response");
[self.mCommArrTot addObject: #"0"];
}
else{
self.dictComm = [self.mButtonsCommentsDict valueForKey:#"comments"];
[self.mCommArr addObject:[self.dictComm valueForKey:#"comment"]];
NSLog(#"count:%d",[self.mCommArr count]);
// NSString *strTot = [NSString stringWithFormat:#"%d",tot];
// [self.mCommArrTot addObject:strTot];
NSLog(#"dictcomm:%#", self.dictComm );
NSLog(#"mcommarr:%#", [self.mCommArr objectAtIndex:indexVal]);
strr = [[NSString alloc]init];
strr = [self.mCommArr objectAtIndex:indexVal];
//NSString *strr = [[NSString stringWithFormat:#"%#", [self.mCommArr objectAtIndex:indexVal]];
// NSArray *arr1 = [self string:strr];
// NSArray *splitArray=[self.mCommArr[0] componentsSeparatedByString:#","];
//[strr componentsSeparatedByString:#","];
// NSLog(#"arrSep:%#", arr1);
//int count = [arr1 count];
//NSLog(#"arrcount:%d", count);
// NSString *strTot = [NSString stringWithFormat:#"%d",count];
// [self.mCommArrTot addObject:strTot];
//NSLog(#"mcommarrtot:%#", [self.mCommArrTot objectAtIndex:indexVal]);
}
// NSLog(#"arrLike:%#", [self.mArrLike objectAtIndex:indexVal]);
// NSLog(#"arrDisLike:%#", [self.mArrLike objectAtIndex:indexVal]);
[self.mPublicFriendTable reloadData];
});
});
}
#catch (NSException *exception) {
NSLog(#"main: Caught %#: %#", [exception name], [exception reason]);
}
#finally {
}
}
It get killed when try to split. Why so, i am not getting. If anyone has faced such situation please guide what is wrong her.
You can split the string into an NSArray like below...
NSString *yourString = #"comment,comment,comment";
NSArray *strArray = [yourString componentsSeparatedByString:#","];
NSLog(#"\n\n Array is ==>> %#",strArray);
"[__NSArrayI componentsSeparatedByString:]:
Your error says you tried to send the above method to NSArray, which doesn't has.
As you want to split the array at index 0. you should probably do as :
NSArray *splitArray=[yourArray[0] componentsSeparatedByString:#","];
Here yourArray is the array that you get from Server.
NSString *strr = [[NSString stringWithFormat:#"%#", [self.mCommArr objectAtIndex:indexVal]];
NSArray *arr2 = [strr componentsSeparatedByString:#","];
I think you are passing str right now, which can be an array (as your error points out).
Let me know the results.

Tread 9: EXC_BAD_ACCESS [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I get this message "Tread 9: EXC_BAD_ACCESS (code=1, address=0x70000010) in this method (but this bug is being created only when in another thread file is being downloaded ):
- (NSMutableDictionary *) getDictionaryAllStatin:(sqlite3*)database
{
if (self._streetsArrey == nil || self._streetsArrey.count <= 0) {
[self getArrayAllStatin:database];
}
/*--------------------------------------------------------------*/
NSMutableDictionary *result1 = [[NSMutableDictionary alloc] init];
for (StreetData *street in _streetsArrey) {
NSString * name = [self deleteContractionWithText:street._name];
NSArray * arr = [name componentsSeparatedByString:#" "];
NSMutableArray *arrm = [[NSMutableArray alloc] initWithArray:arr];
arr = nil;
[arrm addObject:name];
for (NSString *txt in arrm) {
int lengthText = txt.length;
for (int i = 2 ; i <= lengthText; i++) {
NSString * key = [txt substringToIndex:i];
key = [key lowercaseString];
NSMutableDictionary *isSet = [result1 objectForKey:[NSNumber numberWithInt:[key hash]]];
if (isSet == nil) {
isSet = [[NSMutableDictionary alloc]init];
}
[isSet setObject:street forKey:[NSNumber numberWithInt:street._streetId]];
[result1 setObject:isSet forKey:[NSNumber numberWithInt:[key hash]]];
isSet = nil;
key = nil;
}
}
}
NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
for (id key in result1) {
NSMutableDictionary *dictionary = [result1 objectForKey:key];
NSArray *arr = [dictionary allValues];
[result setObject:arr forKey:key];
arr = nil;
[dictionary removeAllObjects];
dictionary = nil;
}
[result1 removeAllObjects];
result1 = nil;
/*--------------------------------------------------------------*/
if (result.count > 0) {
_streetsDictionary = result;
result = nil;
return _streetsDictionary;
}else {
_streetsDictionary = nil;
return nil;
}
}
Why do I get this message?
How can I fix it?
The most likely cause for the crash is trying to access an object that has already been deallocated.
Since it seems that the failure arises on the line:
NSMutableDictionary *isSet = [result1 objectForKey:[NSNumber numberWithInt:[key hash]]];
I would suggest splitting the statement down to its component to try and track down which object could actually be the culprit:
NSInteger h = [key hash];
NSNumber n = [NSNumber numberWithInt:h];
...
but this bug is being created only when in another thread file is being downloaded
Also, check if the downloading code and the crashing code have anything in common. The former might be causing the deallocation of an object used in the second.
Hope it helps.

GDataXml Parsing Issues in iPhone

here this is below the code i need to parse the section and Company Sub data Using GDataXml in iOS.
-<MainList>
<FirstMessage>Right</FirstMessage>
<SecondMessage>Wrong</SecondMessage>
<Status>0</Status>
-<CompanyList>
-<Company>
<Address1>12447 Hedges Run Drive</Address1>
<Address2>#B-1 </Address2>
<City>Lake Ridge</City>
<CompanyName>Starbucks</CompanyName>
<CreatedBy>example#example.com</CreatedBy>
-</Company>
-<Company>
<Address1>12447 Hedges Run Drive</Address1>
<Address2>#B-1 </Address2>
<City>Lake Ridge</City>
<CompanyName>Starbucks</CompanyName>
<CreatedBy>example#example.com</CreatedBy>
-</Company>
</CompanyList>
</MainList>
Here is My Try i am getting every thing fine but i am getting nothing in NSArray arr.
NSArray *channels = [rootElement elementsForName:#"CompanyList"];
for (GDataXMLElement *icon in channels) {
NSArray *arr = [icon elementsForName:#"Company"];
if ([arr count]>0)
{
NSlog(#"Done");
}
}
See this answer, this answer is helpful for u. NSXMLParser example
Hello guys i figured it out as follows
GDataXMLDocument *xmlDocument = [[GDataXMLDocument alloc]initWithData:xmlData options:0 error:&error];
NSLog(#"Error:%#",[error description]);
NSArray *getData = [[xmlDocument rootElement]elementsForName:#"CompanyList"];
NSArray *tempArray = [[NSMutableArray alloc]init];
for(GDataXMLElement *e in getData)
{
NSArray *el = [e elementsForName:#"Company"];
for (GDataXMLElement *e in el) {
static int count =0;
NSString *testString = [[[e elementsForName:#"Address1"] objectAtIndex:0] stringValue];
NSLog(#"Company %d : %#",count+1, testString);
}

Deserializing local NSString of JSON into objects via RestKit (no network download)

Is it possible to deserialize an NSString of JSON into objects via RestKit? I checked the API list here and could not find something that would serve for this purpose. The closest I could find are the various parser classes that return NSDictionary after parsing the input. I assume RestKit uses these parsers after downloading the response so my thinking is that the functionality is available somewhere in RestKit but not exposed publicly.
If I am not missing anything and this functionality is not exposed, what would be the alternatives? Two obvious ones do not look very promising: Get the resulting NSDictionary and try to deserialize myself (effectively reimplementing RestKit) or try to dive into RestKit source and see if this can be somehow exposed (looks tedious and error prone).
Thanks in advance for any help.
PS: The idea is that a string property on a deserialized object is actually the JSON representation of another set of objects (embedded JSON in a sense) and it is deserialized on demand during runtime.
Pretty "simple":
NSString *stringJSON;
...
RKJSONParserJSONKit *parser;
NSError *error= nil;
parser= [[[RKJSONParserJSONKit alloc] init] autorelease];
MyManagedObject *target;
target= [MyManagedObject object];
NSDictionary *objectAsDictionary;
RKObjectMapper* mapper;
objectAsDictionary= [parser objectFromString:stringJSON error:&error];
mapper = [RKObjectMapper mapperWithObject:objectAsDictionary
mappingProvider:[RKObjectManager sharedManager].mappingProvider];
mapper.targetObject = target;
RKObjectMappingResult* result = [mapper performMapping];
NSLog(#"%#", [result asObject]);
As of RestKit 0.20.0-pre2
NSString* JSONString = #"{ \"name\": \"The name\", \"number\": 12345}";
NSString* MIMEType = #"application/json";
NSError* error;
NSData *data = [JSONString dataUsingEncoding:NSUTF8StringEncoding];
id parsedData = [RKMIMETypeSerialization objectFromData:data MIMEType:MIMEType error:&error];
if (parsedData == nil && error) {
// Parser error...
}
AppUser *appUser = [[AppUser alloc] init];
NSDictionary *mappingsDictionary = #{ #"someKeyPath": someMapping };
RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithRepresentation:parsedData mappingsDictionary:mappingsDictionary];
mapper.targetObject = appUser;
NSError *mappingError = nil;
BOOL isMapped = [mapper execute:&mappingError];
if (isMapped && !mappingError) {
// Yay! Mapping finished successfully
NSLog(#"mapper: %#", [mapper representation]);
NSLog(#"firstname is %#", appUser.firstName);
}
This works for Restkit 0.21.0:
NSString* jsonFilePath = [[NSBundle mainBundle] pathForResource:#"fileName"
ofType:#"json"];
NSString* JSONString = [NSString stringWithContentsOfFile:jsonFilePath
encoding:NSUTF8StringEncoding
error:NULL];
NSError* error;
NSData *data = [JSONString dataUsingEncoding:NSUTF8StringEncoding];
id parsedData = [RKMIMETypeSerialization objectFromData:data MIMEType:RKMIMETypeJSON error:&error];
if (parsedData == nil && error) {
// Parser error...
}
//_objectManager is RKObjectManager instance
NSMutableDictionary *mappingsDictionary = [[NSMutableDictionary alloc] init];
for (RKResponseDescriptor *descriptor in _objectManager.responseDescriptors) {
[mappingsDictionary setObject:descriptor.mapping forKey:descriptor.keyPath];
}
RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithRepresentation:parsedData mappingsDictionary:mappingsDictionary];
NSError *mappingError = nil;
BOOL isMapped = [mapper execute:&mappingError];
if (isMapped && !mappingError) {
NSLog(#"result %#",[mapper mappingResult]);
}
This works for Restkit 0.20, using Core Data Entities. It is based in the solution given by #innerself
NSString* jsonFilePath = [[NSBundle mainBundle] pathForResource:#"info-base"
ofType:#"json"];
NSString* JSONString = [NSString stringWithContentsOfFile:jsonFilePath
encoding:NSUTF8StringEncoding
error:NULL];
NSError *error = nil;
NSData *data = [JSONString dataUsingEncoding:NSUTF8StringEncoding];
id parsedData = [RKMIMETypeSerialization objectFromData:data MIMEType:RKMIMETypeJSON error:&error];
if (parsedData == nil && error) {
// Parser error...
NSLog(#"parse error");
}
//_objectManager is RKObjectManager instance
NSMutableDictionary *mappingsDictionary = [[NSMutableDictionary alloc] init];
for (RKResponseDescriptor *descriptor in [RKObjectManager sharedManager].responseDescriptors) {
[mappingsDictionary setObject:descriptor.mapping forKey:descriptor.keyPath];
}
RKManagedObjectMappingOperationDataSource *datasource = [[RKManagedObjectMappingOperationDataSource alloc]
initWithManagedObjectContext:[RKManagedObjectStore defaultStore].persistentStoreManagedObjectContext
cache:[RKManagedObjectStore defaultStore].managedObjectCache];
RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithRepresentation:parsedData
mappingsDictionary:mappingsDictionary];
[mapper setMappingOperationDataSource:datasource];
NSError *mappingError = nil;
BOOL isMapped = [mapper execute:&mappingError];
if (isMapped && !mappingError) {
// data is in [mapper mappingResult]
}
You can see how RestKit does this internally in the RKManagedObjectResponseMapperOperation class.
There are three stages of this operation.
The first is to parse the JSON string into NSDictionarys, NSArrays, etc. This is the easiest part.
id parsedData = [RKMIMETypeSerialization objectFromData:data
MIMEType:RKMIMETypeJSON
error:error];
Next you need to run a mapping operation to convert this data into your NSManagedObjects. This is a bit more involved.
__block NSError *blockError = nil;
__block RKMappingResult *mappingResult = nil;
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
operationQueue.maxConcurrentOperationCount = 1;
[[RKObjectManager sharedManager].managedObjectStore.persistentStoreManagedObjectContext performBlockAndWait:^{
Remember to replace this dictionary with your own mappings. The key [NSNull null] maps this object from the root.
NSDictionary *mappings = #{[NSNull null]: [jotOfflineRequestStatus mapping]};
RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithRepresentation:parsedData
mappingsDictionary:mappings];
RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc]
initWithManagedObjectContext:[RKManagedObjectStore defaultStore].persistentStoreManagedObjectContext
cache:[RKManagedObjectStore defaultStore].managedObjectCache];
dataSource.operationQueue = operationQueue;
dataSource.parentOperation = mapper;
mapper.mappingOperationDataSource = dataSource;
[mapper start];
blockError = mapper.error;
mappingResult = mapper.mappingResult;
}];
You now need to run the tasks that have been put into the operationQueue we created. It is at this stage that connections to existing NSManagedObjects are made.
if ([operationQueue operationCount]) {
[operationQueue waitUntilAllOperationsAreFinished];
}
A more iOS 5+ oriented answer:
NSString* JSONString = jsonString;
NSString* MIMEType = #"application/json";
NSError* error = nil;
id<RKParser> parser = [[RKParserRegistry sharedRegistry] parserForMIMEType:MIMEType];
id parsedData = [parser objectFromString:JSONString error:&error];
if (parsedData == nil && error) {
NSLog(#"ERROR: JSON parsing error");
}
RKObjectMappingProvider* mappingProvider = [RKObjectManager sharedManager].mappingProvider;
RKObjectMapper* mapper = [RKObjectMapper mapperWithObject:parsedData mappingProvider:mappingProvider];
RKObjectMappingResult* result = [mapper performMapping];
if (result) {
NSArray *resultArray = result.asCollection;
MyObject *object = [resultArray lastObject];
NSLog(#"My Object: %#", object);
}
For Restkit 0.22, You can use this code. This returns an RKMappingResult wherein you can enumerate the objects after mapping using the property .array.
- (RKMappingResult *)mapJSONStringWithString:(NSString *)jsonString
{
RKMappingResult *result = nil;
NSError* error;
NSData *data = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
id parsedData = [RKMIMETypeSerialization objectFromData:data MIMEType:RKMIMETypeJSON error:&error];
if (parsedData == nil && error) {
NSLog(#"json mapping error");
}
NSDictionary *mappingsDictionary = #{#"":[CustomMappingClass getMappingForUsers]};
ObjectClass *obj = [ObjectClass new];
RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithRepresentation:parsedData mappingsDictionary:mappingsDictionary];
NSError *mappingError = nil;
mapper.targetObject = obj;
BOOL isMapped = [mapper execute:&mappingError];
if (isMapped && !mappingError) {
result = [mapper mappingResult];
}
return result;
}
Is this not what you're looking for? http://restkit.org/api/0.10.0/Classes/RKJSONParserJSONKit.html
Judging by the views without any answers, it seems this facility does not exist in RestKit yet. Instead of spending more time trying to figure out how to do the mapping, I wrote my own mapper using the output of JsonKit parser and removed the dependency on RestKit (used the builtin classes for network activity). Right now my mapper is not generic (it has a few dependencies on how the objects are laid out and their names in json) but it works for the purposes of the project. I might come back later and turn it into a more generic object mapping library later on.
EDIT: This was selected answer because there was no other answer as of this answer's date (Jan 21, 2012). Since then, I stopped working on iOS and never visited this question again. Now I am selecting Ludovic's answer because of another user's comment and the upvotes for that answer.

Memory Leak according to Instruments

Been running instruments on my app. Its says i am leaking 864bytes & 624bytes from 2 NSCFString and the library responsible is Foundation.
So that leads me to believe thats its not a leak caused by me? Or is it?
Here is the offending method according to instruments. It seems to be a
substringWithRange
that is leaking.
-(void) loadDeckData
{
deckArray =[[NSMutableArray alloc] init];
NSString* path = [[NSBundle mainBundle] pathForResource:#"rugby" ofType:#"txt"
inDirectory:#""];
NSString* data = [NSString stringWithContentsOfFile:path encoding:
NSUTF8StringEncoding error: NULL];
NSString *newString = #"";
NSString *newline = #"\n";
NSString *comma = #",";
int commaCount = 0;
int rangeCount = 0;
NSString *nameHolder = #"";
NSString *infoHolder = #"";
NSMutableArray *statsHolder = [[NSMutableArray alloc] init];
for (int i=0; i<data.length; i++)
{
newString = [data substringWithRange:NSMakeRange(i, 1)];
if ([newString isEqualToString: comma]) //if we find a comma
{
if (commaCount == 0)// if it was the first comma we are parsing the
NAME
{
nameHolder = [data substringWithRange:NSMakeRange(i-
rangeCount, rangeCount)];
}
else if (commaCount == 1)//
{
infoHolder = [data substringWithRange:NSMakeRange(i-
rangeCount, rangeCount)];
//NSLog(infoHolder);
}
else // if we are on to 2nd,3rd,nth comma we are parsing stats
{
NSInteger theValue = [[data
substringWithRange:NSMakeRange(i-rangeCount,rangeCount)]
integerValue];
NSNumber* boxedValue = [NSNumber
numberWithInteger:theValue];
[statsHolder addObject:boxedValue];
}
rangeCount=0;
commaCount++;
}
else if ([newString isEqualToString: newline])
{
NSInteger theValue = [[data substringWithRange:NSMakeRange(i-
rangeCount,rangeCount)] integerValue];
NSNumber* boxedValue = [NSNumber numberWithInteger:theValue];
[statsHolder addObject:boxedValue];
commaCount=0;
rangeCount=0;
Card *myCard = [[Card alloc] init];
myCard.name = nameHolder;
myCard.information = infoHolder;
for (int x = 0; x < [statsHolder count]; x++)
{
[myCard.statsArray addObject:[statsHolder
objectAtIndex:x]];
}
[deckArray addObject:myCard];
[myCard autorelease];
[statsHolder removeAllObjects];
}
else
{
rangeCount++;
}
}
[statsHolder autorelease];
}
Thanks for your advice.
-Code
As Gary's comment suggests this is very difficult to diagnose based on your question.
It's almost certainly a leak caused by you however, I'm afraid.
If you go to the View menu you can open the Extended Detail. This should allow you to view a stack trace of exactly where the leak occurred. This should help diagnose the problem.
When to release deckArray? If deckArray is a class member variable and not nil, should it be released before allocate and initialize memory space?