在Objective-C中是否有与C ++的动态强制转换相同的东西?

时间:2009-09-03 15:32:49

标签: objective-c

如果我有两个类,SubClass和SuperClass:

SuperClass *super = new SuperClass();
SubClass *sub = new SubClass();
SubClass *sub_pointer;

// **The nice one-line cast below**
sub_pointer = dynamic_cast<SubClass*> super;
// Prints NO
printf("Is a subclass: %s\n", sub_pointer ? "YES" : "NO");

sub_pointer = dynamic_cast<SubClass*> sub;
// Prints YES
printf("Is a subclass: %s\n", sub_pointer ? "YES" : "NO");

我可以使用isMemberOfClass在objective-C中完成相同的操作,如下所示:

SuperClass *super = [[SuperClass alloc] init];
SubClass *sub = [[SubClass alloc] init];
SubClass *sub_pointer;
id generic_pointer;

// Not as easy:
generic_pointer = super;
if ([generic_pointer isMemberOfClass:[SubClass class]]) {
  sub_pointer = generic_pointer;
} else {
  sub_pointer = nil;
}
// Logs NO
NSLog(@"Is a subclass: %@", sub_pointer ? @"YES" : @"NO");

generic_pointer = sub;
if ([generic_pointer isMemberOfClass:[SubClass class]]) {
  sub_pointer = generic_pointer;
} else {
  sub_pointer = nil;
}
// Logs YES
NSLog(@"Is a subclass: %@", sub_pointer ? @"YES" : @"NO");

有比这更简单的方法吗?

(PS我知道我不必使用额外的id变量,但是我必须强制执行super to SubClass *,这有时会导致无效的引用,之后我必须清理它。然而,实现不那么罗嗦,而且它在下面)

SuperClass *super = [[SuperClass alloc] init];
SubClass *sub = [[SubClass alloc] init];
SubClass *sub_pointer;

// Not as easy:
sub_pointer = (SubClass*) super;
if (![sub_pointer isMemberOfClass:[SubClass class]]) {
  sub_pointer = nil;
}
// Logs NO
NSLog(@"Is a subclass: %@", sub_pointer ? @"YES" : @"NO");

sub_pointer = (SubClass*) sub;
if (![sub_pointer isMemberOfClass:[SubClass class]]) {
  sub_pointer = nil;
}
// Logs YES
NSLog(@"Is a subclass: %@", sub_pointer ? @"YES" : @"NO");

6 个答案:

答案 0 :(得分:4)

您可以在NSObject上添加类别以添加所需的功能。

//NSObject+DynamicCast.h
@interface NSObject (DynamicCast)
-(id)objectIfMemberOfClass:(Class)aClass;
@end

//NSObject+DynamicCast.m
@implementation NSObject (DynamicCast)
-(id)objectIfMemberOfClass:(Class)aClass;
{
  return [self isMemberOfClass:aClass] ? self : nil;
}
@end

然后你可以这样做:

SuperClass *super = [[SuperClass alloc] init];
SubClass *sub = [[SubClass alloc] init];
SubClass *sub_pointer;
id generic_pointer;

// **The nice one-line cast below**
sub_pointer = [super objectIfMemberOfClass:[SubClass class]];
// Prints NO
printf("Is a subclass: %s\n", sub_pointer ? "YES" : "NO");

sub_pointer = [sub objectIfMemberOfClass:[SubClass class]];
// Prints YES
printf("Is a subclass: %s\n", sub_pointer ? "YES" : "NO");

答案 1 :(得分:4)

我使用宏:

#define DYNAMIC_CAST(x, cls)                                \
  ({                                                        \
    cls *inst_ = (cls *)(x);                                \
    [inst_ isKindOfClass:[cls class]] ? inst_ : nil;        \
  })

我稍微喜欢它在NSObject上使用类别,因为返回的对象是正确的类型(而不是id),尽管我意识到在大多数情况下你只是将它分配给相同类型的变量

答案 2 :(得分:2)

如果您允许将任何C ++投入混合中,您可以避免使用宏并使用模板例程获取正确的类型:

template <typename T, typename U>
inline T* objc_cast(U* instance)
{
    return [instance isMemberOfClass:[T class]] ?
               static_cast<T*>(instance) :
               nil;
}

然后电话会看起来像:

sub_pointer = objc_cast<SubClass>(super);

答案 3 :(得分:1)

xCode的更新版本提供instancetype并允许基于类别的非常简洁的解决方案。使用它看起来像这样:

TypeIWant *const thingIWant = [TypeIWant tryCast: thingIWantToCast];

NSObject

上的类别声明
@interface NSObject (DynamicCast)
// Try a dynamic cast. Return nil if the class isn't compatible.
+(instancetype) tryCast: (id) toCast;

// Check a dynamic cast. Throw if the class isn't compatible.
+(instancetype) checkCast: (id) toCast;
@end

类别实施

@implementation NSObject (DynamicCast)

+(instancetype) tryCast: (id) toCast
{
    return [toCast isKindOfClass: self] ? toCast : nil;
}

+(instancetype) checkCast:(id)toCast
{
    const id casted = [self tryCast: toCast];
    if(!casted)
    {
        [NSException raise: NSInvalidArgumentException format: @"Can't cast %@ to be an %@", toCast, NSStringFromClass(self)];
    }
    return casted;
}

@end

用法

这是一个类方法,可以在任何类上调用NSObject作为超类(基本上,任何类)。

您将tryCast:方法发送到希望投射到,并将您想要的对象作为参数投放。像这样[ClassIWant tryCast: thingIWantCasted]

使用示例

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    UIPopoverController *const popoverDestination =
    [UIStoryboardPopoverSegue tryCast: segue].popoverController;
    if(popoverDestination)
    {
        UITableViewCell *const tableViewSender =
        [UITableViewCell tryCast: sender];
        if(tableViewSender)
        {
            // Things you need to do in this case.
            ...

答案 4 :(得分:0)

我发现我可以使用三元运算符将这一切都放在一行上,但它仍然有点混乱:

SuperClass *super = [[SuperClass alloc] init];
SubClass *sub = [[SubClass alloc] init];
SubClass *sub_pointer;

// One line, but still a bit wordy
sub_pointer = [super isMemberOfClass:[SubClass class]] ? (SubClass*) super : nil;
// Logs NO
NSLog(@"Is a subclass: %@", sub_pointer ? @"YES" : @"NO");

sub_pointer = [sub isMemberOfClass:[SubClass class]] ? (SubClass*) sub : nil;
// Logs YES
NSLog(@"Is a subclass: %@", sub_pointer ? @"YES" : @"NO");

如果我从函数中获取了预期的变量,我将不得不将其缓存在一个id变量中,以使该版本正常工作。

SubClass *sub_pointer;
id generic_pointer;

// One line, but still a bit wordy
generic_pointer = (id) mySuperFunc();
sub_pointer = [generic_pointer isMemberOfClass:[SubClass class]] ? generic_pointer : nil;
// Logs NO
NSLog(@"Is a subclass: %@", sub_pointer ? @"YES" : @"NO");

generic_pointer = (id) mySubFunc();
sub_pointer = [generic_pointer isMemberOfClass:[SubClass class]] ? generic_pointer : nil;
// Logs YES
NSLog(@"Is a subclass: %@", sub_pointer ? @"YES" : @"NO");

答案 5 :(得分:0)

基本上......

id sub_pointer = [foo isMemberOfClass:AClass] ? foo : nil;
NSLog(@"Is a subclass: %i", sub_pointer!=nil ); 

似乎没有多么罗嗦。

相关问题