声明变量符合协议时的标准模式是给它“任何对象”类型,id. 声明一个变量既具有特定类型又符合协议通常是多余的——我稍后会解释原因。现在,让我们谈谈 type 的变量,一些协议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只是 type id<Adder。请记住,这意味着 of 的类型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,只需更改 to 的主体getAdder即可return [[Calculator alloc] init]!一条线。您的其余代码保持完全相同。在这种情况下,您隐藏getAdder了从其余代码返回的实例的真实类型。信息隐藏使重构更容易。
最后,我答应解释为什么类似的东西Abacus <Adder> *a = ...通常是多余的。你在这里说的是“是符合a的实例。” 但是您(和编译器)已经知道符合- 它就在接口声明中!正如rmaddy 指出的那样,在某些情况下,您想要谈论一个实例,它要么是给定的类,要么是其子类,并且还要指定它符合协议,但这种情况很少见,并且通常同时指定两者不需要类和协议一致性。AbacusAdderAbacusAdder
有关更多信息,请查看 Apple 的使用 Protcols指南。