[英]What is the purpose of declaring a protocol for a variable?
I have been reading about Protocols on Objective-C but I cannot grasp this: 我一直在阅读有关Objective-C的协议,但我不能理解这一点:
Consider this line 考虑这条线
Person <CoordinateSupport> *person = [[Person alloc] init];
What is the purpose of declaring the variable to conform to the protocol CoordinateSupport
? 声明变量符合协议
CoordinateSupport
的目的是什么? Is this something just for compile time, so Xcode can warn me if I assign something different to person
or is there any purpose at run time? 这是否仅用于编译时,如果我
person
分配了其他内容,或者在运行时有任何目的,Xcode会警告我吗?
I cannot see how a variable can conform to a protocol. 我看不到变量如何符合协议。 OK, a class is easy to see, because you can have a protocol defining methods that you want some class to follow but an ivar?
好的,一个类很容易看到,因为您可以有一个协议来定义您希望某个类遵循但ivar可以遵循的方法。
I am not seeing it. 我没看到
The standard pattern when declaring that a variable conforms to a protocol is to give it the "any object" type, id
. 声明变量符合协议时的标准模式是为其赋予“任何对象”类型
id
。 Declaring that a variable both has a specific type and conforms to a protocol is typically redundant – I'll explain why later. 声明变量既具有特定类型又符合协议通常是多余的–稍后将解释原因。 For now, let's talk about variables of type
id<P>
, where P
is some protocol, and why they're useful. 现在,让我们讨论一下
id<P>
类型的变量(其中P
是某种协议)以及它们为什么有用的原因。 This type should be read as "an instance of any class that conforms to P
." 该类型应理解为“符合
P
的任何类的实例”。
To concretize the discussion that follows, let's define a protocol: 为了具体化下面的讨论,让我们定义一个协议:
@protocol Adder
- (NSInteger)add:(NSInteger)a to:(NSInteger)b;
@end
I cannot see how a variable can conform to a protocol.
我看不到变量如何符合协议。
This one is easy. 这很容易。 A variable conforms to an Objective-C protocol when it represents an instance of a class that implements all of the required methods in the protocol.
当变量表示实现协议中所有必需方法的类的实例时,它符合Objective-C协议。
@interface Abacus : NSObject <Adder>
@end
@implementation Abacus
- (NSInteger)add:(NSInteger)a to:(NSInteger)b { return a + b; }
- (NSInteger)beadCount { return 91; }
@end
Given this Abacus
class, you could, of course, create a new Abacus
: 鉴于此
Abacus
课程,您当然可以创建一个新的Abacus
:
Abacus *a = [[Abacus alloc] init];
NSLog(@"%ld", (long)[a add:5 to:6]); // 11
NSLog(@"%ld", (long)[a beadCount]); // 91
But you could also declare a
to just be of type id<Adder
. 但您也可以声明
a
只是id<Adder
类型。 Remember, that means the type of a
is "an instance of any class that conforms to 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'
The compiler complains because all we said about the type of a
is that it is a class that conforms to Adder
, and nowhere in the Adder
protocol do we say anything about a method named beadCount
. 编译器会抱怨,因为我们所有说过的类型
a
是,它是符合一类Adder
,无处在Adder
协议做我们说一个叫什么方法beadCount
。
What is the purpose of declaring the variable to conform to [a protocol]?
声明变量符合[协议]的目的是什么?
The purpose is for information hiding. 目的是为了隐藏信息。 When you want a class that conforms to
Adder
, you don't need to care about what the actual class is – you just get an id<Adder>
. 当您想要一个符合
Adder
的类时,无需关心实际的类是什么–您只需获得一个id<Adder>
。 Imagine that Abacus
is a system class, and you've written the following code: 假设
Abacus
是一个系统类,您已经编写了以下代码:
- (Abacus *)getAdder { return [[Abacus alloc] init]; }
- (void)doWork {
Abacus *a = [self getAdder];
// Do lots of adding...
}
Then, in iOS 42, Apple comes up with a new innovation – the Calculator
class! 然后,在iOS 42中,Apple提出了一项新的创新–
Calculator
类! Your friends tell you that Calculator
adds two numbers together more than twice as fast as Abacus
, and all the cool kids are using it! 您的朋友告诉您,
Calculator
将两个数字相加的速度是Abacus
两倍多,而所有很酷的孩子都在使用它! You decide to refactor your code, but you realize that not only do you have to change the return type of getAdder
, but also the types of all the variables to which you assign the return value of getAdder
! 您决定重构代码,但是您意识到,不仅必须更改
getAdder
的返回类型,而且还必须更改为其分配getAdder
的返回值的所有变量的类型! Lame. 瘸。 What if you had done this instead:
如果您改为这样做,该怎么办:
- (id<Adder>)getAdder { return [[Abacus alloc] init]; }
- (void)doWork {
id<Adder> *a = [self getAdder];
// Do lots of adding...
}
Now, when you want to migrate to Calculator
, you just need to change the body of getAdder
to return [[Calculator alloc] init]
and you're done! 现在,当您要迁移到
Calculator
,只需要更改getAdder
的主体以return [[Calculator alloc] init]
就可以了! One line. 一条线。 The rest of your code stays exactly the same.
您的其余代码完全相同。 In that case, you have hidden the true type of the instance returned from
getAdder
from the rest of your code. 在这种情况下,您将从其余代码中隐藏了从
getAdder
返回的实例的真实类型。 Information hiding makes refactoring easier. 信息隐藏使重构更加容易。
Lastly, I promised to explain why something like Abacus <Adder> *a = ...
is usually redundant. 最后,我答应解释一下为什么
Abacus <Adder> *a = ...
的东西通常是多余的。 What you're saying here is " a
is an instance of Abacus
that conforms to Adder
." 您在这里所说的是“
a
是符合Adder
的Abacus
实例”。 But you (and the compiler) already know that Abacus
conforms to Adder
– it's right there in the interface declaration! 但是您(和编译器)已经知道
Abacus
符合Adder
-它就在接口声明中! As rmaddy points out , there are some cases where you want to talk about an instance that is either a given class, or a subclass thereof, and also specify that it conforms to a protocol, but those situations are rare, and most often specifying both a class and protocol conformance is unneeded. 正如rmaddy指出的那样 ,在某些情况下,您想谈论一个既是给定类,又是其子类的实例,并指定它符合协议,但是这种情况很少见,通常都指定两者不需要类和协议一致性。
For more information, check out Apple's Working with Protcols guide. 有关更多信息,请查阅Apple的“使用Protcols”指南。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.