为变量声明协议的目的是什么?
What is the purpose of declaring a protocol for a variable?
我一直在阅读有关 Objective-C 的协议,但我无法理解:
考虑这一行
Person <CoordinateSupport> *person = [[Person alloc] init];
声明变量符合协议的目的是什么CoordinateSupport
?这是编译时的东西吗,所以如果我给 person
分配了不同的东西,Xcode 可以警告我,或者在 运行 时间有什么目的吗?
我看不出变量如何符合协议。好吧,一个 class 很容易看到,因为你可以有一个协议定义你想要一些 class 遵循的方法,但是一个 ivar?
我没看到。
声明一个变量符合协议时的标准模式是给它 "any object" 类型,id
。声明一个变量都具有特定类型 和 都符合协议通常是多余的——我稍后会解释原因。现在,我们来谈谈 id<P>
类型的变量,其中 P
是某种协议,以及它们为何有用。这种类型应该读作 "an instance of any class that conforms to P
."
为了使下面的讨论具体化,让我们定义一个协议:
@protocol Adder
- (NSInteger)add:(NSInteger)a to:(NSInteger)b;
@end
I cannot see how a variable can conform to a protocol.
这个很简单。当变量表示实现协议中所有必需方法的 class 实例时,它符合 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
class,您当然可以创建一个新的 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
的类型是 "an instance of any class that conforms to 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
的 class,而在 Adder
协议中没有任何地方我们对名为 beadCount
的方法有什么看法吗?
What is the purpose of declaring the variable to conform to [a protocol]?
目的是为了信息隐藏。当你想要一个符合 Adder
的 class 时,你不需要关心实际的 class 是什么——你只需要得到一个 id<Adder>
。假设 Abacus
是一个系统 class,并且您编写了以下代码:
- (Abacus *)getAdder { return [[Abacus alloc] init]; }
- (void)doWork {
Abacus *a = [self getAdder];
// Do lots of adding...
}
然后,在 iOS 42 年,Apple 提出了一项新的创新 – Calculator
class!您的朋友告诉您,Calculator
将两个数字相加的速度是 Abacus
的两倍多,所有酷孩子都在使用它!您决定重构您的代码,但您意识到您不仅需要更改 getAdder
的 return 类型,而且还需要更改分配给 return 的所有变量的类型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]
即可!一条线。您的其余代码保持完全相同。在这种情况下,您 隐藏了 实例的真实类型 return 来自 getAdder
的其余代码。信息隐藏使重构更容易。
最后,我答应解释为什么像 Abacus <Adder> *a = ...
这样的东西通常是多余的。你在这里说的是“a
是符合 Adder
的 Abacus
的一个实例。”但是您(和编译器)已经知道 Abacus
符合 Adder
– 它就在接口声明中!作为rmaddy points out,有些情况下你想谈论一个实例,它要么是一个给定的class,要么是它的一个子class,并且还指定它符合一个协议,但这些情况很少见,而且通常不需要同时指定 class 和协议一致性。
有关详细信息,请查看 Apple 的 Working with Protcols 指南。
我一直在阅读有关 Objective-C 的协议,但我无法理解:
考虑这一行
Person <CoordinateSupport> *person = [[Person alloc] init];
声明变量符合协议的目的是什么CoordinateSupport
?这是编译时的东西吗,所以如果我给 person
分配了不同的东西,Xcode 可以警告我,或者在 运行 时间有什么目的吗?
我看不出变量如何符合协议。好吧,一个 class 很容易看到,因为你可以有一个协议定义你想要一些 class 遵循的方法,但是一个 ivar?
我没看到。
声明一个变量符合协议时的标准模式是给它 "any object" 类型,id
。声明一个变量都具有特定类型 和 都符合协议通常是多余的——我稍后会解释原因。现在,我们来谈谈 id<P>
类型的变量,其中 P
是某种协议,以及它们为何有用。这种类型应该读作 "an instance of any class that conforms to P
."
为了使下面的讨论具体化,让我们定义一个协议:
@protocol Adder
- (NSInteger)add:(NSInteger)a to:(NSInteger)b;
@end
I cannot see how a variable can conform to a protocol.
这个很简单。当变量表示实现协议中所有必需方法的 class 实例时,它符合 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
class,您当然可以创建一个新的 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
的类型是 "an instance of any class that conforms to 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
的 class,而在 Adder
协议中没有任何地方我们对名为 beadCount
的方法有什么看法吗?
What is the purpose of declaring the variable to conform to [a protocol]?
目的是为了信息隐藏。当你想要一个符合 Adder
的 class 时,你不需要关心实际的 class 是什么——你只需要得到一个 id<Adder>
。假设 Abacus
是一个系统 class,并且您编写了以下代码:
- (Abacus *)getAdder { return [[Abacus alloc] init]; }
- (void)doWork {
Abacus *a = [self getAdder];
// Do lots of adding...
}
然后,在 iOS 42 年,Apple 提出了一项新的创新 – Calculator
class!您的朋友告诉您,Calculator
将两个数字相加的速度是 Abacus
的两倍多,所有酷孩子都在使用它!您决定重构您的代码,但您意识到您不仅需要更改 getAdder
的 return 类型,而且还需要更改分配给 return 的所有变量的类型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]
即可!一条线。您的其余代码保持完全相同。在这种情况下,您 隐藏了 实例的真实类型 return 来自 getAdder
的其余代码。信息隐藏使重构更容易。
最后,我答应解释为什么像 Abacus <Adder> *a = ...
这样的东西通常是多余的。你在这里说的是“a
是符合 Adder
的 Abacus
的一个实例。”但是您(和编译器)已经知道 Abacus
符合 Adder
– 它就在接口声明中!作为rmaddy points out,有些情况下你想谈论一个实例,它要么是一个给定的class,要么是它的一个子class,并且还指定它符合一个协议,但这些情况很少见,而且通常不需要同时指定 class 和协议一致性。
有关详细信息,请查看 Apple 的 Working with Protcols 指南。