14

Is there an Objective-C equivalent of C++'s dynamic_cast?

It can be faked using this:

MyClass *safeObject = [object isKindOfClass: [MyClass class]]
                      ? (MyClass *)originalObject : nil;

But this is a lot of code to type, even if I don't need to type it often.

I am a bit rusty so this might not be quite right, but I believe the equivalent in C++ would be:

MyClass safeObject = dynamic_cast<MyClass>(orginalObject);

The context here is a block where the parameter is defined as a type of a more generic class, but in this block I "know" it's a specific subclass. Nevertheless, I don't want to just cast it blindly with (MyClass *)originalObject and ignore a theoretically possible error.

To be clear, while I'd love a dynamic_cast, I'd be happy with an alternate approach to safely handle this case as well.

Steven Fisher
  • 44,462
  • 20
  • 138
  • 192
  • In the worst case, you could write a macro for this. – JustSid May 11 '12 at 19:56
  • 4
    Objective-C is duck-typed. Quite honestly, "complicated" casts are kinda rowing against the language. – zneak May 11 '12 at 20:04
  • Yes, I'm thinking I probably asked the wrong question here. Whatever question I *should have* asked had an answer of "just use `NSAssert([object isKindOfClass: [MyClass class]], @"blahblah")` because you're not expecting it anyway." :) – Steven Fisher May 11 '12 at 20:07

3 Answers3

20

If you're willing to use Objective-C++, you can write this pretty easily:

template<typename T> inline T* objc_cast(id from) {
    if ([from isKindOfClass:[T class]]) {
        return static_cast<T*>(from);
    }
    return nil;
}

This should behave exactly as dynamic_cast<> except for obj-c objects.


If you want to stick with vanilla Obj-C you can get similar behavior with a class method on NSObject:

@interface NSObject (Cast)
+ (instancetype)cast:(id)from;
@end

@implementation NSObject (Cast)
+ (instancetype)cast:(id)from {
    if ([from isKindOfClass:self]) {
        return from;
    }
    return nil;
}
@end

This version just isn't as nice to use since you have to say something like

UIButton *button = [UIButton cast:someView];

In both versions the resulting value is nil if the cast fails.

Lily Ballard
  • 182,031
  • 33
  • 381
  • 347
  • 1
    This second example is *exactly* what I was looking for. And kind of brilliantly simple. Thank you. – Steven Fisher Aug 29 '12 at 05:04
  • that is a lovely use of the instancetype operator, so the compiler and the Xcode autocomplete can immediately start using the converted type – SystematicFrank Jun 28 '13 at 08:28
  • I'm a conditional operator partisan, which also makes this a one-liner: `+(instancetype)cast:(id)from {return [from isKindOfClass:self] ? from : nil; }` – Scott Marks Aug 20 '22 at 17:37
9

Try this macro:

#define objc_dynamic_cast(obj, cls) \
    ([obj isKindOfClass:(Class)objc_getClass(#cls)] ? (cls *)obj : NULL)

And also don't forget to

#include <objc/runtime.h>

Use it like:

MyClass *safeObject = objc_dynamic_cast(originalObject, MyClass);
Steven Fisher
  • 44,462
  • 20
  • 138
  • 192
  • I hope you don't mind, but I wrapped your macro and added the (cls *) to satisfy a compiler warning. Great job! I hesitated to use a macro here because of Xcode's edit in scope, but it seems to work wonderfully. – Steven Fisher May 11 '12 at 20:05
  • Would you mind adding a line showing how to use this macro (for greener outsiders). Thanks. – Wienke May 11 '12 at 20:13
  • I added one. (H2CO3: If you feel I'm messing up your answer, feel free to revert. Just trying to help future readers!) – Steven Fisher May 11 '12 at 20:16
  • Given the nature of the problem, wouldn't it be better for the code to blow up when the object can't be safely cast? – zoul May 11 '12 at 20:20
  • Yes, I think that's a better answer to the problem. At this point, though, I'm trying to play fair with the question I actually asked. :) – Steven Fisher May 11 '12 at 20:38
  • @StevenFisher no problem, I appreciate it. I don't mind people editin my answers as long as it's not offensive :) –  May 12 '12 at 11:09
  • instead of `(Class)objc_getClass(#cls)` why not just `[cls class]`? – user102008 May 23 '12 at 03:29
  • Because `cls` may not be available at compile-time, in that case OP would get linker errors (yep, these are those nice things called "reflection" and "dynamism" in Obj-C...) –  May 23 '12 at 04:34
  • Nothing's wrong with it. It's still up-voted. But Kevin nailed the desired effect with something simpler. – Steven Fisher Sep 04 '13 at 20:36
3
  1. I don't think there is.
  2. I think the space for a bug is quite small here.
  3. But if you insist, a macro will do fine?
zoul
  • 102,279
  • 44
  • 260
  • 354