声明变量协议的目的是什么?

时间:2017-04-07 15:59:24

标签: objective-c protocols ivar ivars

我一直在阅读有关Objective-C的协议,但我无法理解:

考虑这一行

Person <CoordinateSupport> *person = [[Person alloc] init];

声明变量符合协议CoordinateSupport的目的是什么?这是否仅适用于编译时间,因此如果我为person分配不同的内容或者在运行时是否有任何用途,Xcode可以发出警告?

我无法看到变量如何符合协议。好的,一个类很容易看到,因为你可以有一个协议定义你希望某些类可以遵循的方法但是ivar?

我没有看到它。

1 个答案:

答案 0 :(得分:1)

声明变量符合协议时的标准模式是为其赋予“任何对象”类型id。声明变量都具有特定类型符合协议通常是多余的 - 我将在后面解释原因。现在,我们来谈谈id<P>类型的变量,其中P是一些协议,以及它们为什么有用。此类型应该被理解为“符合P的任何类的实例。”

为了具体化后面的讨论,让我们定义一个协议:

@protocol Adder
- (NSInteger)add:(NSInteger)a to:(NSInteger)b;
@end
  

我无法看到变量如何符合协议。

这个很容易。当变量表示实现协议中所有必需方法的类的实例时,该变量符合Objective-C协议。

@interface Abacus : NSObject <Adder>
@end

@implementation Abacus
- (NSInteger)add:(NSInteger)a to:(NSInteger)b { return a + b; }
- (NSInteger)beadCount { return 91; }
@end

鉴于此Abacus课程,您当然可以创建新的Abacus

Abacus *a = [[Abacus alloc] init];
NSLog(@"%ld", (long)[a add:5 to:6]);  // 11
NSLog(@"%ld", (long)[a beadCount]);   // 91

但您也可以将a声明为id<Adder类型。请记住,这意味着a的类型是“符合Adder的任何类的实例。”

id<Adder> a = [[Abacus alloc] init];
NSLog(@"%ld", (long)[a add:5 to:6]);  // 11
NSLog(@"%ld", (long)[a beadCount]);   // Compile error: No known instance method for selector 'beadCount'

编译器抱怨,因为我们所说的关于a类型的所有内容是它是一个符合Adder的类,并且在Adder协议中没有任何地方我们对名为beadCount的方法。

  

声明变量符合[协议]的目的是什么?

目的是隐藏信息。当你想要一个符合Adder的类时,你不需要关心实际的类是什么 - 你只需要id<Adder>。想象一下,Abacus是一个系统类,你编写了以下代码:

- (Abacus *)getAdder { return [[Abacus alloc] init]; }
- (void)doWork {
    Abacus *a = [self getAdder];
    // Do lots of adding...
}

然后,在iOS 42中,Apple推出了一项新的创新 - Calculator课程!你的朋友告诉你,Calculator将两个数字加在一起的速度是Abacus的两倍,所有很酷的孩子都在使用它!您决定重构代码,但您意识到您不仅需要更改getAdder的返回类型,还要更改为getAdder返回值的所有变量的类型!瘸。如果你这样做了怎么办:

- (id<Adder>)getAdder { return [[Abacus alloc] init]; }
- (void)doWork {
    id<Adder> *a = [self getAdder];
    // Do lots of adding...
}

现在,当您想要迁移到Calculator时,您只需将getAdder的正文更改为return [[Calculator alloc] init]即可!一条线。其余代码保持完全相同。在这种情况下,您从其余代码中隐藏getAdder返回的实例的真实类型。信息隐藏使重构更容易。

最后,我答应解释为什么像Abacus <Adder> *a = ...这样的东西通常是多余的。您在这里说的是“aAbacus符合Adder的实例。”但是你(和编译器)已经知道Abacus符合Adder - 它就在接口声明中!作为rmaddy points out,在某些情况下,您想要讨论某个给定类或其子类的实例,并指定它符合协议,但这些情况很少见,而且最常见指定类和协议一致性是不必要的。

有关详细信息,请查看Apple的Working with Protcols指南。