简体   繁体   English

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

[英]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是符合AdderAbacus实例”。 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM