【问题标题】:Calling an optional method in superclass (Objective-C)在超类中调用可选方法(Objective-C)
【发布时间】:2012-08-23 16:15:59
【问题描述】:

我正在尝试为 NSCoding 协议创建一个通用实现。 代码将被包裹在一个宏中,该宏将实现 NSCoding。 为了实现协议,我们需要两个函数:

-(void)encodeWithCoder:(NSCoder*)coder;
-(id)initWithCoder:(NSCoder*)coder;

initWithCoder 函数的通用实现是:

-(id)initWithCoder:(NSCoder*)coder { 
    if ([super conformsToProtocol:@protocol(NSCoding)]) 
        self = [super initWithCoder:coder];
    else {
        self = [super init];
    }
    if (!self) return self;
    self = [MyGenericCoder initWithCoder:coder forObject:self withClass:[__clazz class]]; 
    return self; 
}

有问题的行是self = [super initWithCoder:coder];,它不会编译,因为当我们在其super 不实现NSCoding 的类中使用时,super 不会响应initWithCoder:。将 super 转换为 NSObject<NSCoding>* 不适用于 LLVM 编译器。

[super performSelector:(initWithCoder:) withObject:coder] 也不起作用,因为 super == self,这将导致无限循环。

如何调用[super initWithCoder:coder] 以触发超类中的函数并且不会生成编译警告/错误?

【问题讨论】:

  • “它不会编译”你大概是指你收到警告?除非您将相关的编译器选项设置为将警告视为错误,否则我认为它至少应该编译。如果你只是将 super 转换为 (id),LLVM 会怎么想?

标签: objective-c inheritance macros nscoding overriding


【解决方案1】:

您可以使用+instancesRespondToSelector: 找出您的超类是否响应选择器,然后直接使用objc_msgSendSuper() 来实际调用它。

#import <objc/message.h>

- (id)initWithCoder:(NSCoder *)coder {
    // Note: use [__clazz superclass] directly because we need the
    // compile-time superclass instead of the runtime superclass.
    if ([[__clazz superclass] instancesRespondToSelector:_cmd]) {
        struct objc_super sup = {self, [__clazz superclass]};
        ((id(*)(struct objc_super *, SEL, NSCoder*))objc_msgSendSuper)(&sup, _cmd, coder);
    } else {
        [super init];
    }
    if (!self) return self;
    self = [MyGenericCoder initWithCoder:coder forObject:self withClass:[__clazz class]];
    return self;
}

【讨论】:

  • My solution 在运行时找到超类。为什么认为有必要在编译时找到它?
  • @TomerShiri:不,它没有。您还引用了__clazz,我假设它是传递给宏的Class。这就是编译时方面。谨慎是避免试图根据self 来解决这个问题。
【解决方案2】:

如何调用 [super initWithCoder:coder] 以触发超类中的函数并且不会生成编译警告/错误?

只需创建宏的两种变体——一种用于超类采用NSCoding 的类型,另一种用于不采用NSCoding 的类型。

要么,要么从您自己抽象细节并从中间类型派生,这些中间类型从您的基础中抽象出条件并采用NSCopying - 然后您可以在任何此类类型上调用initWithCoder:

【讨论】:

    【解决方案3】:
    #import <objc/runtime.h>
    -(id)initWithCoder:(NSCoder*)coder {
        Class superclass = class_getSuperclass([__clazz class]);
        SEL constructor = @selector(initWithCoder:);
        if (class_conformsToProtocol(superclass,@protocol(NSCoding))) {
            self = class_getMethodImplementation(superclass,constructor)(self,constructor,coder);
        }
        else {
            self = [super init];
        }
        if (!self) return self;
        self = [MyGenericCoder initWithCoder:coder forObject:self withClass:[__clazz class]];
        return self;
    }
    

    【讨论】:

      猜你喜欢
      • 2011-12-21
      • 1970-01-01
      • 1970-01-01
      • 2010-12-11
      • 2013-08-20
      • 1970-01-01
      • 1970-01-01
      • 2012-04-04
      相关资源
      最近更新 更多