1
votes

There are some similar questions that already exist on StackOverflow. I did check them out, and in most cases it returns nil because the NSMutableArray has not been initialised. But in my case I did initialise it.

Here's part of my code :

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    self.storedTimeZones = [[NSMutableArray alloc] init];
    NSData *storedData = [[NSUserDefaults standardUserDefaults] dataForKey:@"timeZones"];

    if (storedData != nil ) {
        self.storedTimeZones = [NSKeyedUnarchiver unarchiveObjectWithData:storedData];
    }

    NSString *str = [self.smallRegionArray[indexPath.row] stringByReplacingOccurrencesOfString:@"_" withString:@" "];
    [self.storedTimeZones addObject: str];
    NSLog(str); //the string was printed successfully.
    NSLog(@"%lu", (unsigned long)self.storedTimeZones.count); //'0' was printed
}

update @Caleb was right, [NSKeyedUnarchiver unarchiveObjectWithData:storedData returned nil. I solved it by doing this:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    self.storedTimeZones = [[NSMutableArray alloc] init];
    NSData *storedData = [[NSUserDefaults standardUserDefaults] dataForKey:@"timeZones"];
    NSMutableArray *ary = [NSKeyedUnarchiver unarchiveObjectWithData:storedData];

    if (ary != nil ) {
        self.storedTimeZones = ary;
    }

    NSString *str = [self.smallRegionArray[indexPath.row] stringByReplacingOccurrencesOfString:@"_" withString:@" "];
    [self.storedTimeZones addObject: str];
    NSLog(str);
    NSLog(@"%lu", (unsigned long)self.storedTimeZones.count); //Now it prints `1`
}
2
It doesn't throw an unrecognized selector exception? - trojanfoe
@trojanfoe lol now I got the unrecognized selector exception - hklel
I'm glad my answer helped, but you still have a big bug: unarchiveObjectWithData will give you an immutable array. If you expect self.storedTimeZones to be mutable, you should make a mutable copy: [[NSKeyedUnarchiver unarchiveObjectWithData:storedData] mutableCopy] - Caleb
@Caleb Thanks for reminding. I added mutableCopy as you suggested but still got the unrecognized selector sent to instance error when I run it. (just like what @trojanfoe predicted) - hklel
@AlexLing Again, you'll want to examine the actual object that self.storedTimeZones points to. I provided debugger commands in my answer. When you know what type of object you're dealing with, you'll understand why you're getting the exception and you should be able to figure out how you get that object instead of the mutable array that you expected. - Caleb

2 Answers

6
votes

But in my case I did initialise it.

You did, but then you replaced it. Here's the initializing line:

self.storedTimeZones = [[NSMutableArray alloc] init];

but then you assign a different value:

self.storedTimeZones = [NSKeyedUnarchiver unarchiveObjectWithData:storedData];

and at this point, self.storedTimeZones may be either a non-mutable instance of NSArray, or some entirely different object, or nil. I'd guess it's the latter since no exception is thrown when you call -addObject:. That the count is 0 also makes sense if self.storedTimeZones is nil, since the result of messaging nil is nil or 0 or NO, depending on what type you expect.

All you really need to do to properly diagnose the problem is to examine self.storedTimeZones near your last NSLog statement. Set a breakpoint and look at the contents of that property. Useful commands will be:

po self.storedTimeZones
po [self.storedTimeZones class]
1
votes

You're trying to assign the object to the entire mutable array, you're not adding a object to it.

That's not the way it works.

Try this:

[self.storedTimeZones addObject: [NSKeyedUnarchiver unarchiveObjectWithData:storedData]];

Or this

[self.storedTimeZones addObjectsFromArray:[NSKeyedUnarchiver unarchiveObjectWithData:storedData]];

Or this

self.storedTimeZones = [NSArray alloc]; // Note: not a mutable array [self.storedTimeZones initWithArray:[NSKeyedUnarchiver unarchiveObjectWithData:storedData]];

You've never told us if the contents of the keyedUnarchiver is an array, but we expect it is.

In either case, if this is the only time you add info to the array, you don't need to make it mutable.

Good luck.