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.
userIdis not being retained by your singleton. The value is owned bypersonDictionary, which is owned bylistOfPeopleDictionaries, which is an autoreleased return value from[responseString JSONValue]. When the autorelease pool is purged the whole chain is released includinguserId.The solution is to retain userId, either by doing
or
usernamehas the same issue, but probably isn’t crashing as you’re holding on to it in the object which is callinginitWithUserName:.