Know Your Class: How to Determine an Object’s Class in Objective-C

Programming in Objective-C is a pleasure. Its dynamic messaging systems allows us to do a lot of little messaging tricks that we could not do in other languages. However, when it comes down to it Objective-C code will crash if you send a message to an object that does not handle it, just as Java code will crash if you try to call a function from an object that doesn’t know how to deal with it. Thus, it is important to know what class you are dealing with, especially in certain strange situations.

For example:

- (void) longImageRequestFromS3 {
     ASINetworkQueue* q = [ASINetworkQueue alloc] init];
     [q setRequestDidFinishSelector:@selector(requestFinished:)];
     [q setQueueDidFinishSelector:@selector(qFinished:)];
     [q setDelegate:self];
     for (int i = 0; i < [imageURLS count]; i++) {
        ASIHTTPRequest* r = [[ASIHTTPRequets alloc] initWithURL:[imageURL objectAtIndex:i]];
        [r setTag:i];
        [q addOperation:r];
     }

     [q go];
}

- (void) requestFinished:(ASIHTTPRequest *)r {
    UIImage* image = [UIImage imageWithData:[r responseData]];
    [yourPrivateMutableArray replaceObjectAtIndex:[r tag] withObject:image];
}

- (void) qFinished:(ASINetworkQueue *)q {
    [yourTable reloadData];
}

- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
   /* norma tableView stuff */
  [yourMagicalCell setSomeImageViewsImage:[yourPrivateMutableArray objectAtIndex:[indexPath row]]];
  return yourMagicalCell;
}

That code sample is far from perfect and is designed to show a point. Let’s walk through it. If all goes well each cell’s someImageView will have an image from an array of images set, but what happens if the ASIHTTPRequest that gets the image fails. Let’s assume that there are a total of ten requests in the queue and only nine succeed. Guess what? Your app crashes. Why? Because you will have tried to use an ASIHTTPRequest as a UIImage, sending a number of message to an instance that cannot handle them.

So the problem is that we need a way to make sure that we are not trying to use an non UIImage as a UIImage. Your first instinct might be to try something like this:

- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
   /* norma tableView stuff */
   if ([[yourPrivateMutableArray objectAtIndex:[indexPath row]] isKindOfClass:[UIImage class]] {
     [yourMagicalCell setSomeImageViewsImage:[yourPrivateMutableArray objectAtIndex:[indexPath row]]];
   }
return yourMagicalCell;
}

Before the more picky of you mention it: yes, this sample applies to isMemberOfClass as well. I don’t know exactly what causes it (I have some theories that I will not float here) but at times that if check can actually pass even though the object will not be able to respond to UIImage’s messages and will crash your app.

However, there is a better way:

- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
/* norma tableView stuff */
  if ([[yourPrivateMutableArray objectAtIndex:[indexPath row]] respondsToSelector:@selector(CGImage)] {
    [yourMagicalCell setSomeImageViewsImage:[yourPrivateMutableArray objectAtIndex:[indexPath row]]];
  }
return yourMagicalCell;
}

The difference here is that we are no longer checking the class of the object, but rather if it responds to a specific message (CGImage in this case). This is a less error prone way to solve the problem. On a more opinionated note, I like this way of looking at the problem a lot, since it takes advantage of Objective-C’s concept of messaging.

Hope this helps you, if you have any feedback I am on Twitter and Google+.

Comments are closed.