unrecognized selector

Discussion in 'iOS Programming' started by larswik, Jun 20, 2012.

  1. larswik macrumors 68000

    Joined:
    Sep 8, 2006
    #1
    I've been down this road before. I am adding a blank cell to my tableview when the user presses the add button. The cell that is created is a custom cell with a UITextField. So I am adding an empty string object to my locationKeys mutableArray (my data source for the tableview). It works the first time but crashes when I try to add another item to the array.

    Code:
    -(void)addindexRow{
        int rowForInsert = locationKeys.count;
    
        [locationKeys insertObject:@"" atIndex:rowForInsert];
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowForInsert inSection:0];
        NSArray* path = [NSArray arrayWithObject:indexPath];
    
        [self.tableView beginUpdates];
        [self.tableView insertRowsAtIndexPaths:path withRowAnimation:UITableViewRowAnimationBottom];
        [self.tableView endUpdates];
        
        [self.tableView reloadData];
    
    }
    
    My question as I am working through this is the error. Is the "unrecognized selector" referring to the locationKeys mutableArray which is the receiver or the instance, or is it the item that I am trying to send to it that is not there? Which in this case is just an empty string?

    I am baffled why it works the first time and not the second?

     
  2. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #2
    I googled __NSArrayI and discovered it's the class name for an immutable array (which I suspected anyway). The corresponding name for a mutable array is __NSArrayM. I mention this because googling your error message (or parts of it) is a long-standing way to learn how others have seen and solved the issue. It's also a good way to get an idea of how common an error is.

    So the likely problem is you're trying to send a message to an immutable array, which only a mutable array can respond to.

    You should look carefully at how the locationKeys ivar is being managed (assigned, retrieved, and generally used). Example, is there a copy attribute on a property that refers to the ivar? If so, you'll get an immutable copy, which would explain how locationKeys got an immutable array reference.

    If there's no property that refers to locationKeys, then make sure every place it's assigned a value, it's being assigned a mutable array.
     
  3. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #3
    I use google all the time. This time I searched for a the definition that I mentioned

    I was unsure if it meant the sender or the reviver was the problem. I did find the error and it was a dumb mistake. In a different Method I was trying to use 'allKeys' of a dict to populate an NSMutableArray and allKeys return type was just an NSArray. So I tried to type cast it like this.

    Code:
    locationKeys = (NSMutableArray*) [selectedClient allKeys];
    
    The error message went away and I thought I learned something new, which I did, but what I learned was not to do that. End result was I changed it to this and it solved the problem. I am guessing you can't type cast objects like you can with primitive data types. I spent an hour looking through the code but it didn't pop out at me that this was the problem area. Now I know.

    Code:
    locationKeys = [NSMutableArray arrayWithArray:[selectedClient allKeys]];
    
     
  4. MattInOz macrumors 68030

    MattInOz

    Joined:
    Jan 19, 2006
    Location:
    Sydney
    #4
    I'd normally use -mutableCopy, like...
    Code:
    locationKeys = [[selectedClient allKeys] mutableCopy];
    
    now wondering if there is a big difference between them and if there is one that's considered to have a better "code smell"?
     
  5. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #5
    Fair enough. You probably got a zillion hits for that part of the error message, which would be less than enlightening.

    Here's a summary of how to interpret "unrecognized selector" errors.

    First, it's always the receiver. It can't be the sender, because the sender isn't being asked to do anything. The receiver is being asked to find a method for the message, and that method is then being executed. So the "instance" in "sent to instance" is always going to mean the receiver.

    Second, in this error message (which I've colorized):
    Code:
    2012-06-20 15:04:23.415 Got Gear[13215:f803] -[[COLOR="Red"]__NSArrayI[/COLOR] [COLOR="SeaGreen"]insertObject:atIndex:[/COLOR]]: unrecognized selector sent to instance 0x6818720
    
    The red always identifies the receiver's class[1]. It should be the actual class, which may be part of a class cluster, or even a proxy class. The actual class is usually one of the important clues, so is worth including in a google search.

    The green identifies the message selector being sent. That may be worth including in a google search. It depends on what the actual class is identified as. Here, I started by googling just the class name, because I suspected it was going to be an immutable array. Once that was determined, it became obvious that an immutable array won't have a method for the insertObject:atIndex: selector, so including the selector wouldn't have found a better answer. The problem was already sufficiently identified simply by knowing the object's class was an immutable array. With that information in hand, you did the right thing: looked at your code. And boom, you found the mistake and fixed it. Congrats, problem solved.


    By the way, you're right that casting an object pointer won't change the actual type of the object. It can't, because the actual type, i.e. the runtime behavior, isn't determined at all by the declared type (i.e. the type of the variable).

    In plain C, you also can't change the type of a pointed-at operand simply be casting the pointer. Example:
    Code:
    float f = 3.14;
    int * p = (int *) &f;
    int i = *p;
    
    This has no effect on the pointed-at value. It just lies to the compiler, and tells it to interpret the pointed-at value as if it were an int. Since the pointed-at bits-and-bytes aren't really an int, you get a very different result than, say:
    Code:
    float f = 3.14;
    int i = (int) f;
    
    In the object-pointer case, the actual runtime type of an object (i.e it's actual class) is determined by its isa class pointer (which is now opaque, and accessed by functions in 10.5 and higher). But that's getting into the deep innards of objects, and for that it's best to read the Objective-C Runtime Reference.



    [1] There may be some exception to this rule, but not that I've ever seen.

    ----------

    -mutableCopy returns an object you own.
    NSMutableArray arrayWithArray: returns an object you don't own.

    So the one with the better smell depends on how ephemeral the object reference ought to be.
     
  6. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #6
    "First, it's always the receiver."

    This line I won't forget and now will better help me solve these. The error is rare for me to get these days but it still happens. It was interesting that trying to type cast that object caused the error to go away making me think I solved the issue. I must have satisfied something with the error message even though that code would even have worked.


    MattInOz: When I started learning programming I picked up a habit of always alloc/init objects I want to stay around. If I need the ivar to stay around I make it a global variable or instance variable I believe is the correct what of saying it.

    I rarely use setters and getters or inheritance. The whole nonatomic, retain, strong, copy thing confused me when I was learning Object C, but alloc / init I got right away and it stuck with me and that is how I created objects to use that stayed around.

    At some point I need to double back on my education and learn somethings I am missing, like Class Methods too.

    Thanks guys!
     
  7. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #7
    copy and mutableCopy make clear your purpose. Add an autorelease to the line if that's important.

    I usually only see stringWithString: and arrayWithArray: in code written by beginners.
     

Share This Page