Objective c 什么';在Objective C中,确保对象符合接口/协议的最可靠和可读的方法是什么?

Objective c 什么';在Objective C中,确保对象符合接口/协议的最可靠和可读的方法是什么?,objective-c,interface,protocols,Objective C,Interface,Protocols,我试图将代码转换为接口(或Objective C术语中的协议),而不是实现 在调用对象上的方法以防止崩溃之前,检查对象是否符合协议是至关重要的 三种方式 在编译器中 运行时 两者 最佳解决方案。。。当然是第一名? 我认为最好的方法是在编译器中: 如果你搞砸了怎么办 删除StopRotocol:/respondsToSelector:样板文件 在运行时,如果您犯了错误,那就太晚了-您所能做的最好的事情就是不执行代码/显示错误 但我看到很多代码都是在运行时执行的。为什么? 这是一个可读性问题吗?

我试图将代码转换为接口(或Objective C术语中的协议),而不是实现

在调用对象上的方法以防止崩溃之前,检查对象是否符合协议是至关重要的

三种方式

  • 在编译器中
  • 运行时
  • 两者
  • 最佳解决方案。。。当然是第一名?

    我认为最好的方法是在编译器中:

    • 如果你搞砸了怎么办
    • 删除StopRotocol:/respondsToSelector:样板文件
    • 在运行时,如果您犯了错误,那就太晚了-您所能做的最好的事情就是不执行代码/显示错误
    但我看到很多代码都是在运行时执行的。为什么?

    这是一个可读性问题吗?到处都需要
    id

    我的问题

    确保对象符合接口/协议的最可靠和可读的方法是什么

    代码

    @interface ReportController : NSObject {
        id generator;
        id sender;
        id report;
    }
    
    @implementation ReportController
    
    -(id)initWithReportGenerator:(id)generator_
                    reportSender:(id)sender_ {
        // Usual init stuff
        generator = generator_;
        sender = sender_;
        return self;
    }
    
    -(void)generateAndSend {
        if ([generator conformsToProtocol:@protocol(ReportGenerator)] &&
            [sender conformsToProtocol:@protocol(ReportSender)]) {
            report = [generator generate];
            [sender sendReport:report];
        } else {
            [NSException raise:NSInternalInconsistencyException format:@"Objects didn't respond to protocols..."];
        }
    }
    
    @end
    
    1。签入编译器

    @interface ReportController : NSObject {
        id <ReportGenerator> generator;
        id <ReportSender> sender;
        id report;
    }
    
    @implementation ReportController
    
    -(id)initWithReportGenerator:(id <ReportGenerator>)generator_
                    reportSender:(id <ReportSender>)sender_ {
        // Usual init stuff
        generator = generator_;
        sender = sender_;
        return self;
    }
    
    -(void)generateAndSend {
        report = [generator generate];
        [sender sendReport:report];
    }
    
    @end
    

    您应该在协议中创建具有@required类型的方法。 所以,无论什么类想要与这个协议签订契约,都必须实现这些方法。
    它肯定会确保所需的方法仅在编译时可用。

    您应该同时使用这两种方法。考虑一下:

    @protocol Proto
    - (void)someFunction;
    @end
    
    @interface C : NSObject
    - (void)proto:(id<Proto>)p;
    @end
    
    // ...
    NSString *s = @"moo";
    id i = s;
    
    C *c = [[C alloc] init];
    [c proto:s]; // warns
    [c proto:i]; // doesn't warn
    
    @协议协议协议
    -(空)某些功能;
    @结束
    @接口C:NSObject
    -(void)proto:(id)p;
    @结束
    // ...
    NSString*s=@“moo”;
    id i=s;
    C*C=[[C alloc]init];
    [c proto:s];//警告
    [c proto:i];//没有警告
    
    Objective-C和Cocoa过于动态,无法在编译时通常检查这些内容(
    NSProxy
    standins,动态添加方法和协议的类等)。

    在编译时捕获尽可能多的此类错误是很好的,但仅此一点是不够的。

    只要您不使用plain
    id
    作为类型,编译器至少会在您在编译时出错时向您发出警告。因此,您应该对代码示例#1感到满意

    当然,有时您可能会被迫使用从不受您控制的子系统获得的
    id
    对象。在这种情况下,您可以将对象强制转换回您认为它具有的类型(例如,
    id
    ),但如果您首先执行运行时检查,通常情况下会更好。安全总比后悔好


    最后一点注意:如果您的协议有可选部分(用
    @optional
    关键字声明),那么对于这些部分,您显然只能执行运行时检查。apurv提到的
    @required
    关键字只有在您希望在协议声明中明确说明时才是必需的(默认情况下,协议的部分是必需的),或者如果您混合了可选部分和必需部分。

    @required
    是默认值,它通常不会确保在编译时存在方法。@GeorgFritzsche:您能解释一下“通常不会确保在编译时存在方法”的意思吗?我的印象是,只要您使用显式类型,并且不使用普通的
    id
    ,编译器就会检查类是否符合协议。@herzube:一件事是,它只是一个类型检查,在存在像
    id
    这样的非严格转换时,基本上对任何东西都没有帮助。然后,您通常无法确保方法在编译时的存在-实现可能在动态加载的代码(共享库等)、不同的项目/流程(例如分布式对象)中,或者您可能在运行时构建了类。这一点非常好。真不敢相信我之前没看到。所有这些协议检查加上类型检查现在完全有意义了。谢谢还有一个问题-你会如何处理协议不符合-什么都不做?抛出异常?还是返回一个错误?@John:这个问题很难回答,这取决于上下文。