[NSArray count] Problem (exception thrown)

Discussion in 'iPhone/iPad Programming' started by Fuzzball27, Feb 15, 2012.

  1. macrumors newbie

    Joined:
    Aug 8, 2011
    #1
    I have a problem with this section of code:

    Code:
    NSArray *array = [[[NSArray alloc] init] autorelease];
        array = [self getSavedChanges];
        
        NSLog(@"%u", [array count]);
    Here is the getSavedChanges method:

    Code:
    - (NSArray *)getSavedChanges
    {
        NSArray *array = [[[NSArray alloc] init] autorelease];
        array = [NSKeyedUnarchiver unarchiveObjectWithFile:pathInDocumentDirectory(@"points.data")];
        return array;
    }
    Here is the pathInDocumentDirectory() function:

    Code:
    NSString *pathInDocumentDirectory(NSString *filename)
    {
        NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentDirectory = [documentDirectories objectAtIndex:0];
        return [documentDirectory stringByAppendingPathComponent:filename];
    }
    The problem that I am experiencing is that after my objects are unarchived and added to the NSArray, they are sent the message "count." This happens when I send "count" to the NSArray. I don't want the count of the objects IN the NSArray, I want the count of the NSArray itself... Why are the objects within the NSArray being sent "count"?

    Just in case you were wondering, here is the code from the debugger where the exception is thrown. "MapPoint" is the name of the class of the unarchived objects:

    Code:
    2012-02-15 15:32:01.342 MyLocation[16596:11c03] -[MapPoint count]: unrecognized selector sent to instance 0x6d8d540
    2012-02-15 15:32:01.362 MyLocation[16596:11c03] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MapPoint count]: unrecognized selector sent to instance 0x6d8d540'
    *** First throw call stack:
    (0x157d052 0x139dd0a 0x157eced 0x14e3f00 0x14e3ce2 0x2c53 0x23864e 0x198a73 0x198ce2 0x198ea8 0x19fd9a 0x170be6 0x1718a6 0x180743 0x1811f8 0x174aa9 0x1380fa9 0x15511c5 0x14b6022 0x14b490a 0x14b3db4 0x14b3ccb 0x1712a7 0x172a9b 0x1d02 0x1c75)
    terminate called throwing an exception(gdb) 
     
  2. Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #2
    Why do you keep creating autoreleased array instances that you don't use and then instantly assign object to that variable?
     
  3. macrumors 65816

    jnoxx

    Joined:
    Dec 29, 2010
    Location:
    Aartselaar // Antwerp // Belgium
    #3
    You are having heavy memory leaks.. you can assign a pointer with a return method, instead of assigning a new one again..
     
  4. macrumors Pentium

    KnightWRX

    Joined:
    Jan 28, 2009
    Location:
    Quebec, Canada
    #4
    You're not checking anything. Look at NSKeyedUnarchiver and the unarchiveObjectWithFile: method, it returns an id type, not an NSArray type.

    The method says it does this :

    Are you sure points.data contained an object of type NSArray ? Have you tried checking whatever got return's class property to make sure ?
     
  5. macrumors 68000

    Sydde

    Joined:
    Aug 17, 2009
    #5
    Did you in fact archive a NSArray of MapPoint objects? Please show the code that does that.
     
  6. thread starter macrumors newbie

    Joined:
    Aug 8, 2011
    #6
    I'm not quite sure what you guys are saying (I'm obviously new with OOP), but does this fix it?
    Code:
    NSArray *array = [self getSavedChanges];
    
    //and
    
        NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithFile:pathInDocumentDirectory(@"points.data")];
    
    I archived the MapPoints one by one from the annotations array in an MKMapView instance. If I use archiveRootObject (MKMapView.annotations) it crashes, and if I archive the last object of the same array it crashes. So I settled with this:

    Code:
    - (void)save
    {
        for (int i = 0; i + 1 < worldView.annotations.count; i++) {
            [NSKeyedArchiver archiveRootObject:[worldView.annotations objectAtIndex:i] toFile:pathInDocumentDirectory(@"points.data")];
            NSLog(@"Archived: %@", [[worldView.annotations objectAtIndex:i] title]);
        }
    }
     
  7. macrumors Pentium

    KnightWRX

    Joined:
    Jan 28, 2009
    Location:
    Quebec, Canada
    #7
    You do know you can check an object's class at runtime right ?

    Code:
    id array;
    
    array = returnFromFunctionOrMethod();
    
    if([array isKindOfClass: [NSArray class]])
        NSLog(@"Well gosh darn it is an array!");
    else
        NSLog(@"We got something else...");
    
     
  8. Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #8
    Code:
    NSArray *array = [[[NSArray alloc] init] autorelease];
        array = [self getSavedChanges];
    }
    In the first code you are declaring a new pointer variable (array) and creating a new object that you assign to that variable. You then instantly assign a totally different object to it. Clearly the creation of the autoreleased array is pointless.

    As for fixing it it solves the problem I pointed out. It does not solve the problem that array may well be nil if points.data does not exist or does not contain a valid datastructure for the keyed unarchiver.

    It appears you have little formal training. I would suggest your read up on and consider defensive programming. Consider what could happen in each possible outcome of a method call. For example in the above you may have a valid NSArray instance. Or you may have nil. You completely ignore the second case which is quite possibly giving you the error you see.
     
  9. macrumors 6502

    Joined:
    Jun 17, 2007
    #9
    Or you may have an instance of something else.

    Just because a variable is of type NSArray, doesn't mean that you have an NSArray in it.

    For debugging purposes, try adding this line:

    Code:
    NSLog(@"Class of contents of 'array' variable: %@", NSStringFromClass([array class]));
    Add that line just after the array = [self getSavedChanges]; line. Then find the line it prints out in the debug console. That'll tell you what class you've actually got.

    Amorya
     
  10. macrumors Pentium

    KnightWRX

    Joined:
    Jan 28, 2009
    Location:
    Quebec, Canada
    #10
    Correct me if I'm wrong, but according to the NSObject reference, this line doesn't work. class is a Class method, not an instance method. You can't call it on an instance. To do what you want to do, you'd use the instance method className :

    https://developer.apple.com/library...asses/nsobject_Class/Reference/Reference.html
    So really, your code would look like this instead :

    Code:
    NSLog(@"Class of contents of 'array' variable: %@", [array className]);
    But really, the OP wants the snippet I posted. Testing what you got is always good practice, especially when loading objects dynamically like that. If you get what you want, go ahead and do your stuff, if not, fail gracefully or try to recover. isKindOfClass permits that kind of coding practice which robbie was talking about, defensive programming.
     
  11. Fuzzball27, Feb 16, 2012
    Last edited: Feb 16, 2012

    thread starter macrumors newbie

    Joined:
    Aug 8, 2011
    #11
    Thank you all for the help.
    [array class] returned an instance of MapPoint meaning that I screwed up in the archiving process. I realized this as I was creating my last post: rather than archiving the root object then retrieving the root object, I was archiving each individual map point to points.data then using UnarchiveObjectWithFile as if I had archived the root object to points.data. UnarchiveObjectWithFile was only getting one object from points.data rather than magically creating an array from all objects in points.data.

    So I've edited the saveChanges method to this:
    Code:
    - (void)save
    {
        NSLog(@"%@", worldView.annotations);
        NSMutableArray *array = [NSMutableArray arrayWithArray:worldView.annotations];
        [array removeObjectAtIndex:array.count - 1];
        NSLog(@"%@", array);
        
        [NSKeyedArchiver archiveRootObject:array toFile:pathInDocumentDirectory(@"points.data")];
    }
     

Share This Page