6

I often find myself asserting that an object "isKindOfClass" of some class in Objective-C. I do it like this:

NSAssert([obj isKindOfClass:[AClass class]], @"%@ should be kind of class %@", obj, [[AClass class] description]);

I'm wondering about the best way to make a short-cut for it. I'm thinking about defining a macro, something like:

#define NSClassAssert(obj, class)  NSAssert([obj isKindOfClass:class], @"%@ should be of class %@", obj, [class description])

I'm worried that this might cause some nasty intractable compile errors or run-time problems, is there anything fundamentally wrong with doing it this way, is there a plain better way to do it?

apaderno
  • 28,547
  • 16
  • 75
  • 90
jbat100
  • 16,757
  • 4
  • 45
  • 70
  • 3
    Why are you making these assertions at all? – rob mayoff Nov 22 '11 at 13:36
  • Given how tolerant and dynamic objective c is, I like to make code "abuse proof". Instead of having an unrecognized selector message, I have a message telling me which class the object was expected to be. – jbat100 Nov 22 '11 at 13:47
  • 3
    Prefixing your own macro with 'NS' would be misleading. – jlehr Nov 22 '11 at 14:47
  • You should look into using categories. Here's a link to the iOS Developer Library: http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html – user2285278 Jul 03 '13 at 01:55
  • Yep, do not prefix anything you do with "NS", "UI", or any of the other common Apple prefixes. Choose a (relatively) unique prefix for your project or organization. – Hot Licks Jan 27 '14 at 20:28
  • You're going to have trouble with that macro. Change `class` to, eg, `clasz` for the substitution variable, so it doesn't get confused with the `class` property name. Should be something like `...@"%@ should be of class %@", obj, [clasz class])`. – Hot Licks Jan 27 '14 at 20:33
  • If you're expecting a particular class at runtime, then it's a great idea to check it and fail if the unexpected happens. Better to fail early and near the problem than see unexpected consequences later. The performance hit will generally be worth it. I often use this type of macro when I am doing casts. – TJez Apr 14 '14 at 10:06

3 Answers3

3

One has to ask exactly why you want to do this in the first place. You said:

Given how tolerant and dynamic objective c is, I like to make code "abuse proof". Instead of having an unrecognized selector message, I have a message telling me which class the object was expected to be.

If you pass an object to a method that does not match the type declared in the signature, i.e., you do something like this:

- (void)doSomething:(NSArray *)array { /* Do something * }

- (void)doSomethingElse
{
    NSString *string = @"my string";
    [self doSomething:string];
}

you'll get a warning, so you should know at compile time that the object types don't match.

Of course, this doesn't protect you when using a more generic type, such as id, but if you're worried about unrecognized selectors, you really should check to see if a object responds to that selector, instead of using an assert:

- (id)doSomething:(id)obj
{
    if ([obj respondsToSelector:@selector(calculateSomething)]) {
        return [obj calculateSomething];
    } else {
        NSLog(@"%@ does not respond to calculateSomething", obj);
    }
}

Or use protocols to check for the availability of methods.


However, that said, yes, your macro will work for your intended purpose.

mipadi
  • 398,885
  • 90
  • 523
  • 479
  • Thanks for your comments, it's mainly to debug networking code which uses NSArchiver and co. to pack information data, with several people working on it, it's nice to know straight away when something's unexpected. – jbat100 Nov 22 '11 at 20:59
  • 1
    As soon as an "id" is involved this is not really helpful. And ids are not only involved with networking code et.al., but also with array and dictionary entries (and lots of other places). I use a macro like this for "defensive" programming. – radiospiel Jan 27 '14 at 20:20
0

NSParameterAssert saves you from writing a string:

NSParameterAssert([view isKindOfClass:UITableView.class]);

Results in:

Terminating app due to uncaught exception 'NSInternalInconsistencyException',
reason: 'Invalid parameter not satisfying: [view isKindOfClass:UITableView.class]'
malhal
  • 26,330
  • 7
  • 115
  • 133
-1

Instead of having an unrecognized selector message, I have a message telling me which class the object was expected to be.

Testing whether or not an object is of a specific class helps tremendously in making sure a piece of code is invoked with the right kind of objects; for example with an Array of NSWhatEvers. Objective-C doesn't provide that out of the box, and your code looks fine; I use a macro like that in my code which is

#define NSIsKindOf(obj, klass)  NSAssert([obj isKindOfClass:class], @"%@ should be of class %s but is a %@", obj, #klass, [obj class])

If you want to go the duck typing route - i.e. send a selector to an object of unknown origin - then respondsToSelector:, as @mipadi points out, is the way to go.

radiospiel
  • 2,450
  • 21
  • 28
  • this does not add anything new to the topic and the second sentence is a comment. – vikingosegundo Jan 27 '14 at 20:25
  • Actually, I'd just use `[var class]`. `description` is implicit with `%@`. – Hot Licks Jan 27 '14 at 20:30
  • @vikingosegundo: I tend to disagree. The accepted answer states more or less that it doesn't make sense to use the OP's macro, and gives lots of unsolicited advice, when the OP just wanted to know whether anyone would see any problem with his/her macro. Well, I don't, and it that sense my answer I contradict the accepted answer. – radiospiel Jan 27 '14 at 20:41
  • sure you disagree. you don't explain, why your way is to be preferred. and the accepted answer shows best practices, checking for the abilities to handle a message is the standard way in objc. – vikingosegundo Jan 28 '14 at 01:31
  • @vikingosegundo The OP wants his/her code to be "abuse proof". For me that is asserting when it is abused, not to go along with an object as best as one could. – radiospiel Jan 29 '14 at 09:57
  • In a language that supports messaging, message forwarding and protocols such an assert must be considered abuse. – vikingosegundo Jan 29 '14 at 10:05