Какова цель объявления протокола для переменной?

Я читал о протоколах на Objective-C, но я не могу понять это:

Рассмотрим эту строку

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

Какова цель объявления переменной для соответствия протоколу CoordinateSupport? Это что-то только для времени компиляции, поэтому XCode может предупредить меня, если я назначу что-то другое person или есть какая-то цель во время выполнения?

Я не вижу, как переменная может соответствовать протоколу. Хорошо, класс легко увидеть, потому что у вас может быть протокол, определяющий методы, которым вы хотите, чтобы какой-то класс следовал, кроме ивара?

Я не вижу этого.

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 = ... обычно избыточно. То, что вы говорите, здесь " a это пример Abacus что соответствует Adder "Но вы (и компилятор) уже знаете, что Abacus соответствует Adder - это прямо в объявлении интерфейса! Как указывает rmaddy, в некоторых случаях вы хотите поговорить об экземпляре, который является либо данным классом, либо его подклассом, а также указать, что он соответствует протоколу, но такие ситуации встречаются редко, и чаще всего указываются оба соответствие класса и протокола не требуется.

Для получения дополнительной информации ознакомьтесь с руководством Apple по работе с протоколами.

Другие вопросы по тегам