NSMutableDictionary random EXC_BAD_ACCESS - objective-c

Any idea why code below throws EXC_BAD_ACCESS randomly (not always). Sometime when userid is same but we yet to figure out more cases. I understand that setObject:o is unnecessary when object already exists in the dictionary, but even if used, it should not have crashed.
mDict is NSMutableDictionary
-(void) addUser:(NSString *)userid address:(NSString *)address {
if(nil == userid || nil == address) return;
User *o = [mDict objectForKey:userid];
if(!o)
o = [User new];
o.userid = userid;
o.address = address;
[mDict setObject:o forKey:o.userid];
}

Related

Using GCD and Core Data causes crash

I retrieve data back from our server and I need to process it.
For each key, I create a NSManagedObject. Each object is created in the same context. I am using Magical Record.
-(id)init {
if (self = [super init]){
self.context = [NSManagedObjectContext MR_contextForCurrentThread];
}
return self;
}
Threading:
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_t group = dispatch_group_create();
for (id key in boundariesDictionary) {
dispatch_group_async(group, queue, ^{
DLog(#"nsthread: %#", [NSThread currentThread]);
NSString *boundaryIDString;
if ([key isKindOfClass:[NSString class]]) {
boundaryIDString = key;
}
else if ([key isKindOfClass:[NSNumber class]]) {
boundaryIDString = [key stringValue];
}
if (boundaryIDString) {
DLog(#"boundaryIDString: %#", boundaryIDString)
NSDictionary *boundaryDictionary = [boundariesDictionary objectForKey:key];
Boundary *boundary = [Boundary MR_findFirstWithPredicate:[NSPredicate predicateWithFormat:#"boundaryID == %# AND api == %#", [NSNumber numberWithInteger:[boundaryIDString integerValue]], self.serverCall.API] inContext:self.context];
if ([boundaryDictionary objectForKey:AVI_NAME]) {
if (boundary == nil) {
DLog(#"creating boundary %#", boundaryIDString);
boundary = [Boundary MR_createInContext:self.context];
boundary.boundaryID = [NSNumber numberWithInteger:[boundaryIDString integerValue]];
}
}
boundary = [self processBoundary:boundary fromBoundaryDictionary:boundaryDictionary];
}
}
}
[self processBoundary] just takes the dictionary and sets it to the managed object's attributes.
if ([boundaryDictionary objectForKey:#"name"]) {
boundary.name = [boundaryDictionary objectForKey:#"name"];
}
//more data processing
This is causing an error though:
*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFSet: 0x1776f7f0> was mutated while being enumerated.'
It runs fine if I don't use the same context for each thread.
I don't understand what set other then the NSDictionary boundariesDictionary that i'm enumerating through. I am not mutating boundariesDictionary at all, only copying the data into core data.
When I PO the object (0x1776f7f0 in this case), I get a list of Boundary objects in a set. Those Boundary objects would only exist in the NSManagedObjectContext "set", I don't add them to an NSArray, NSDictionary, or NSSet. But I don't believe I enumerate over that set. I do mutate it by creating new boundary objects to it.
I think there is something going on that I don't understand or quite grasp yet.
Any ideas?
UPDATE:
for (id key in boundariesDictionary) {
NSString *boundaryIDString;
if ([key isKindOfClass:[NSString class]]) {
boundaryIDString = key;
}
else if ([key isKindOfClass:[NSNumber class]]) {
boundaryIDString = [key stringValue];
}
if (boundaryIDString) {
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
DLog(#"saveWithBlock thread: %#", [NSThread currentThread]);
NSDictionary *boundaryDictionary = [boundariesDictionary objectForKey:key];
Boundary *boundary = [Boundary MR_findFirstWithPredicate:[NSPredicate predicateWithFormat:#"boundaryID == %# AND api == %#", [NSNumber numberWithInteger:[boundaryIDString integerValue]], self.serverCall.API] inContext:localContext];
if ([boundaryDictionary objectForKey:AVI_NAME]) {
if (boundary == nil) {
boundary = [Boundary MR_createInContext:localContext];
boundary.boundaryID = [NSNumber numberWithInteger:[boundaryIDString integerValue]];
}
boundary = [self processBoundary:boundary fromBoundaryDictionary:boundaryDictionary];
if (boundary == nil) {
//Prompt Error
}
else {
for (NSNumber *groupID in groupIDs) {
if ([groupID isKindOfClass:[NSNumber class]]) {
Group *group = [Group MR_findFirstWithPredicate:[NSPredicate predicateWithFormat:#"groupID == %# OR groupID == 0 AND api == %#", groupID, self.serverCall.API]];
if (group != nil) {
group.lastUpdated = [NSDate date];
[group addBoundariesObject:boundary];
}
else {
DLog(#"group %# DNE", groupID);
}
}
}
}
}
} completion:^(BOOL success, NSError *error) {
DLog(#"saveWithBlock completion Block | time: %f", [[NSDate date] timeIntervalSinceDate:startTime]);
}];
}
}
So for my Group 29 should see all the boundaries I'm creating, but its not. Its inconsistent. Sometimes sees all, sometimes some, and sometimes none.
Also, I often see
NO CHANGES IN ** BACKGROUND SAVING (ROOT) ** CONTEXT - NOT SAVING
in the log. Its also inconsistent on how many of these messages I see, while the context that do save will insert more then 1 object.
Not sure if that is how it should be behaving, seems like it should be a 1-to-1 ratio if each block has its own context and each block only creates 1 object.
I log each thread ID, and it is creating a new thread for each block. No thread ID is being logged twice, so the threads shouldn't be being reused.
Managed object contexts are not thread safe, you must not use them or any managed objects/sets/data structures/whatever they might return from different queues or threads.
I don't know if magical record supports it, but you should definitely be using queue containment for your contexts, and then you have to do everything within the context of the MOC's private queue.
If you use thread containment, you must guarantee that the context and any managed objects created by the context are always serially accessed, which you're absolutely not doing in the code above.
You're issue is that gcd queues a threads are not a one to one mapping. GCD reuses threads, and thus your are likely crossing thread boundaries unknowingly here. My suggestion is to simply create a new context and stop using contextForCurrentThread. I wrote more details about the issues on my blog. ContextForCurrentThread will be deleted in an upcoming release.

Singleton object memory issue in objective c

I've been stuck on this one for a while and any advice would be greatly appreciated. I've created a singleton object called SharedUser that represents the user who is currently using the app. This SharedUser object has two NSStrings (for username and userId) and an NSArray (for the company departments to which the user belongs). The user is initialized upon login with a method called initWithUsername. Inside this method the username is used to parse the user and get the user's unique id number and the the user's departments.
My problem is as follows: when I get the user through the sharedUser method at a later point in the app, the userId string is empty and causes an exc_bad_access error when I try to use it. I know that the userId string is being initialized though because I have observed this happening in the debugger. However, strangely enough, the username string still exists for the same object that is lacking the userId string. I'm very confused as to how the memory behind userId could be released while username would still hang around.
My SharedUser class looks like this:
#implementation SharedUser
#synthesize userId;
#synthesize username;
#synthesize departmentIds;
static SharedUser *sharedUser = nil;
+(SharedUser *)sharedUser {
#synchronized(self) {
if (!sharedUser) {
sharedUser=[[self alloc] init];
}
}
return sharedUser;
}
+ (id)allocWithZone:(NSZone *)zone {
#synchronized(self) {
if (sharedUser == nil) {
sharedUser = [super allocWithZone:zone];
return sharedUser;
}
}
return nil;
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)retain {
return self;
}
- (unsigned)retainCount {
return NSUIntegerMax;
}
- (oneway void)release {
// never release
}
- (id)autorelease {
//do nothing
}
- (id) init {
#synchronized(self) {
[super init];
username = #"";
userId = #"";
departmentIds = nil;
return self;
}
}
- (void) initWithUserName:(NSString *) loginName {
username = loginName;
//create a request and a connection
NSURL *url = [NSURL URLWithString:#"http://blah.com/url/for/the/json/"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request startSynchronous];
NSError *error = [request error];
if (!error) {
NSString *responseString = [request responseString];
NSArray *listOfPeopleDictionaries = [responseString JSONValue];
for (NSDictionary *personDictionary in listOfPeopleDictionaries) {
if ([loginName isEqualToString:[personDictionary objectForKey:#"username"]]) {
// Set the user id
userId = [personDictionary objectForKey:#"id"];
// Set the user's department
NSArray *departmentsArray = [personDictionary objectForKey:#"organizations"];
for (NSDictionary *department in departmentsArray) {
[departmentIds addObject:[department objectForKey:#"id"]];
}
}
}
}
else {
//give error
}
}
- (void)dealloc {
// Should never be called
[username release];
[userId release];
[departmentIds release];
[super dealloc];
}
The following code would cause an exc_bad_access error on the third line.
SharedUser *user = [SharedUser sharedUser];
NSLog(#"%#", user.username);
NSLog(#"%#", user.userId);
That said, my question is where is the memory behind the userId string being released and what do I do to fix it? I'm brand new to stack overflow so please be gentle.
userId is not being retained by your singleton. The value is owned by personDictionary, which is owned by listOfPeopleDictionaries, which is an autoreleased return value from [responseString JSONValue]. When the autorelease pool is purged the whole chain is released including userId.
The solution is to retain userId, either by doing
userId = [[personDictionary objectForKey:#"id"] retain];
or
self.userId = [personDictionary objectForKey:#"id"]
username has the same issue, but probably isn't crashing as you're holding on to it in the object which is calling initWithUserName:.
I would probably go with another solution: "normal" class, that offers one shared instance, while it doesn't bother about all the memory (non-)management
And I want to propose the new grand central dispatch approach
#implementation User
#synthesize userId;
#synthesize username;
#synthesize departmentIds;
+ (User *)sharedUser {
static User *_sharedUser = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedUser = [[self alloc] init…];
//what ever is needed to get a proper shared user
});
return _sharedUser;
}
#end
More is not needed, if you are ok with a less strict version of a singleton.
This is also much easier to handle in Unit-testing.
User *sharedUser = [User sharedUser];
to fix your code, please try
username = [loginName copy]; //or retain, if you use retain in ur property
instead of
username = loginName;

How to parse json specific fields using objective-c

I'm trying to parse a very simple json object with 1 value. (ocUnit test below)
- (void) testHatCartParseWithValidRefId {
NSString* data = #"{\"refid\":999}";
Cart* obj = [HatCartParseJson parseJsonAndReturnObject:data];
STAssertTrue([obj.refid isEqualToString:#"999"], #"fail");
}
In the implementation everything fails when I add the line to pull it from either key or index. How should I pull this from the json input? Please keep in mind I need this json parse (not string) the actual code I'm working with is a large set of JSON data.
+ (Cart *) parseJsonAndReturnObject:(NSString *)json
{
NSArray* cart = [json JSONValue];
for (NSDictionary* item in cart) {
Cart* obj = [[Cart alloc] init];
//NSString* refid = [item objectAtIndex:0];
//NSString* refid = [item objectForKey:#"refid"];
[obj setRefid:#"999"];
return obj;
}
return nil;
}
Thank you in advance
You are expecting that the return value of JSONValue is an NSArray, which in this case it isn't.
So, you must do a check if the return value is actually an NSArray, and if it is, then iterate through the collection, otherwise check if it's an NSDictionary, and if it is, then return the Cart object with the refid from the NSDictionary. If all of this fails, then just return nil.
As a side point, according to Apple's Object Ownership Policy, you should return autorelease-d objects from methods whose names do not contain the words "alloc", "new" or "copy". This would be one such method where you'd return an autorelease-d object.
+ (Cart *) parseJsonAndReturnObject:(NSString *)json
{
id cart = [json JSONValue];
NSString* refid = nil;
if([cart isKindOfClass:[NSArray class]]) {
refid = [[cart objectAtIndex:0] objectForKey:#"refid"];
} else if([cart isKindOfClass:[NSDictionary class]]) {
refid = [cart objectForKey:#"refid"];
}
if(refid) {
Cart* c = [[Cart alloc] init];
[c setRefid:refid];
return [c autorelease];
}
return nil;
}

Need help in finding a memory leak in copying Sqlite Database to Mutable array

I seem to be having a 1.19 KB leak somewhere in the following code. opening up the call tree in instruments, I have narrowed down the leaks to the following:
61.8% of the leaks are coming from +[NSString stringWithUTF8String:]
38.1% of the leaks are coming from +[NSNumber numberWithDouble:]
-(void) readMinesFromDatabase
{
NSLog(#" Setup the database object");
sqlite3 *database;
// Init the Array
northernMines = [[NSMutableArray alloc] init];
nCentralMines = [[NSMutableArray alloc] init];
centralMines = [[NSMutableArray alloc] init];
southernMines = [[NSMutableArray alloc] init];
//NSLog(#" pre if statement");
// Open the database from the users filessytem
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
// Setup the SQL Statement and compile it for faster access
const char *sqlStatement = "SELECT * FROM mines";
//NSLog(#"pre 2nd if statement");
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) != SQLITE_OK)
{
NSLog( #"Error: Failed to prepare stmt with message %s", sqlite3_errmsg(database));
}
else
{
// Loop through the results and add them to the feeds array
NSLog(#"pre loop");
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
NSString *name;
NSString *com;
NSString *county;
NSNumber *lat;
NSNumber *longit;
// Read the data from the result row
//deals with null strings in the name and commodity fields of the database
//NSLog(#"ered the loop");
if (sqlite3_column_text(compiledStatement, 3) != NULL) {
name = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 3)];
}
else {
name = #"";
}
if (sqlite3_column_text(compiledStatement, 10) != NULL) {
com = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 10)];
}
else {
com = #"";
}
//latitude and longitudes
lat = [NSNumber numberWithDouble:(double )sqlite3_column_double(compiledStatement, 4)];
longit = [NSNumber numberWithDouble:(double )sqlite3_column_double(compiledStatement, 5)];
//NSLog(#"long %#",longit);
// Create a new object with the data from the database
Mine *mine = [[Mine alloc] initWithMineName:name latitudeInitial:lat longitudeInitial:longit commodity:com];
// Add the object to the animals Array
county = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 8)];
if([county isEqualToString:#"Butte"] || [county isEqualToString:#"Plumas"] || [county isEqualToString:#"Yuba"] || [county isEqualToString:#"Sierra"])
{
[northernMines addObject:mine];
}
else if([county isEqualToString:#"Nevada" ]|| [county isEqualToString:#"Placer"] || [county isEqualToString:#"El Dorado"] || [county isEqualToString:#"Sutter"])
{
[nCentralMines addObject:mine];
}
else if([county isEqualToString:#"Amador"] || [county isEqualToString:#"Sacramento"] || [county isEqualToString:#"Calaveras"] || [county isEqualToString:#"San Joaquin"] || [county isEqualToString:#"Stanislaus"])
{
[centralMines addObject:mine];
}
else if([county isEqualToString:#"Tuolumne"] ||[county isEqualToString:#"Mariposa"] || [county isEqualToString:#"Madera"] || [county isEqualToString:#"Merced"])
{
[southernMines addObject:mine];
}
else
{
}
[mine release];
//[name release];
//[com release];
//[county release];
//[lat release];
//[longit release];
}
NSLog(#"done with loop");
//[mines addObject:#"nil"];
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
the mine object implementation file is:
#import "Mine.h"
#implementation Mine
#synthesize mineName, latitudeInitial, longitudeInitial, commodity;
-(id)initWithMineName:(NSString *)n latitudeInitial:(NSNumber *)l longitudeInitial:(NSNumber *)g commodity:(NSString *)c
{
self.mineName = n;
self.latitudeInitial = l;
self.longitudeInitial = g;
self.commodity = c;
return self;
}
#end
Well, it is a little hard to tell because of the indentation where the function ends (is this the entire function?) but I believe you are forgetting to release the 4 arrays that you are allocating at the beginning of the function. By leaking the arrays you are also leaking their contents.
Edit - for the initializer code you added:
I am not sure if this has anything to do with the memory leaks but I noticed that the initializer is not implemented correctly:
you should call init on super and update self (more details here)
of this I am not 100% sure, but I think you should not send messages (call methods) to the current object from inside the init method, and by using the dot notation you are actually calling the setter methods (but again, I am not really sure of this one)
I suppose that the properties are declared as (retain) and that you release them in the dealloc method. Which would be correct.
I can't really see anything else wrong with the code you showed. Maybe the problem is not exactly here (just an idea).
Edit 2 - example initializer
You should spend some time reading the Apple documentation, it has plenty of examples.
Here is my version (I hope it doesn't have too many mistakes):
-(id)initWithMineName:(NSString *)n latitudeInitial:(NSNumber *)l longitudeInitial:(NSNumber *)g commodity:(NSString *)c {
// Assign self to value returned by super's designated initializer
// Designated initializer for NSObject is init
self = [super init];
if (self) {
mineName = [n retain];
latitudeInitial = [l retain];
longitudeInitial = [g retain];
commodity = [c retain];
}
return self;
}
Just a thought, but could the memory leak be in the Mine object initializer ( -[Mine initWithMineName: latitudeInitial: longitudeInitial: commodity:] ) ??

Memory leak at NSObject allocation

HI, I am getting memory leak at NSObject allocation i.e.,
ContactDTO* contactDTO = [[ContactDTO alloc] init];
Code:
+(ContactDTO*) getContactDTOForId:(NSString*) contactId
{
NSString* homeMail =#"";
NSString* workMail=#"";
NSString *lastNameString=#"";
NSString *firstNameString=#"";
firstNameString = [AddressBookUtil getValueForProperty:kABPersonFirstNameProperty forContact:contactId];
lastNameString = [AddressBookUtil getValueForProperty:kABPersonLastNameProperty forContact:contactId];
ABRecordID contactIntId = [contactId intValue];
ABRecordRef person = ABAddressBookGetPersonWithRecordID(addressBook, contactIntId);
ABMultiValueRef emailMultiValue =(NSString *)ABRecordCopyValue(person, kABPersonEmailProperty);
for(CFIndex j=0;j<ABMultiValueGetCount(emailMultiValue);j++)
{
NSString* curentTypeLabel =(NSString *)ABMultiValueCopyLabelAtIndex(emailMultiValue,j);
if([curentTypeLabel isEqualToString:#"_$!<Home>!$_"]==YES)
{
NSString* currentEmail =(NSString *)ABMultiValueCopyValueAtIndex(emailMultiValue,j);
if([currentEmail isEqualToString:nil]==NO)
{
homeMail = [currentEmail copy];
}
}
if([curentTypeLabel isEqualToString:#"_$!<Work>!$_"]==YES)
{
NSString* currentEmail =(NSString *)ABMultiValueCopyValueAtIndex(emailMultiValue,j);
if([currentEmail isEqualToString:nil]==NO)
{
workMail = [currentEmail copy];
}
}
}
ContactDTO* contactDTO = [[ContactDTO alloc] init];
contactDTO.firstName = firstNameString;
contactDTO.lastName = lastNameString;
contactDTO.contactId = contactId;
contactDTO.homeEmail = homeMail;
contactDTO.workEmail = workMail;
return [contactDTO autorelease];
}
When reading the email addresses from the Address Book you use ABMultiValueCopyValueAtIndex() which returns a reference which is owned by you (e.g. must be released using CFRelease() by you), as does [obj copy];.
I assume you release the homeMail and workMail in your dealloc method but the copied value from the address book seems leaked in this method.
You've posted three nearly-identical questions pertaining to memory leaks. It might be helpful for you to read through Apple's Memory Management Programming Guide.