KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программирование » Вандад Нахавандипур - iOS. Приемы программирования

Вандад Нахавандипур - iOS. Приемы программирования

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Вандад Нахавандипур, "iOS. Приемы программирования" бесплатно, без регистрации.
Перейти на страницу:

__unused NSString *lastName = [dictionary valueForKey: kLastNameKey];

Но с развитием компилятора LLVM этот код можно сократить, придав ему следующий вид:

NSString *const kFirstNameKey = @"firstName";

NSString *const kLastNameKey = @"lastName";


NSDictionary *dictionary = @{

kFirstNameKey: @"Tim",

kLastNameKey: @"Cook",

};


__unused NSString *firstName = dictionary[kFirstNameKey];

__unused NSString *lastName = dictionary[kLastNameKey];

Как видите, мы инициализируем словарь, давая ключи в фигурных скобках. Точно так же можно поступать и с массивами. Вот как мы обычно создаем и используем массивы:

NSArray *array = [[NSArray alloc] initWithObjects:@"Tim", @"Cook", nil];

__unused NSString *firstItem = [array objectAtIndex:0];

__unused NSString *secondObject = [array objectAtIndex:1];

А теперь, имея возможность подписывать объекты, мы можем сократить этот код следующим образом:

NSArray *array = @[@"Tim", @"Cook"];

__unused NSString *firstItem = array[0];

__unused NSString *secondObject = array[0];


Компилятор LLVM не останавливается и на этом. Вы можете также добавлять подписывание и к собственным классам. Существует два типа подписывания:

подписывание по ключу — действуя таким образом, вы можете задавать внутри объекта значение для того или иного ключа точно так же, как вы делали бы это в словаре. Указывая ключ, вы также можете получать доступ к значениям внутри объекта и считывать их;

 подписывание по индексу — как и при работе с массивами, вы можете устанавливать/получать значения внутри объекта, предоставив для этого объекта индекс. Это целесообразно делать в массивоподобных классах, где элементы естественным образом располагаются в порядке, удобном для индексирования.

Сначала рассмотрим пример подписывания по ключу. Для этого создадим класс под названием Person, имеющий свойства firstName и lastName. Далее мы позволим программисту менять значения этих свойств (имя и фамилию), просто предоставив ключи для этих свойств.

Вам может понадобиться добавить к классу подобный механизм подписывания по ключу, например, по такой причине: имена ваших свойств могут изменяться и вы хотите предоставить программисту возможность устанавливать значения таких свойств, не учитывая, будут ли имена этих свойств впоследствии изменяться. В противном случае программисту лучше будет использовать свойства напрямую. Другая причина реализации подписывания по ключу — стремление скрыть точную реализацию/объявление ваших свойств от программиста и закрыть программисту прямой доступ к этим свойствам.

Чтобы обеспечить поддержку подписывания по ключу в ваших собственных классах, вы должны реализовать в вашем классе два следующих метода и записать сигнатуры методов в файле заголовков этого класса. В противном случае компилятор не узнает, что в вашем классе поддерживается подписывание по ключу.


#import <Foundation/Foundation.h>


/* Мы будем использовать их как ключи для наших свойств firstName

и lastName, так что если имена наших свойств firstName и lastName

в будущем изменятся в реализации, нам не придется ничего переделывать

и наш класс останется работоспособным, поскольку мы сможем просто

изменить значения этих констант в нашем файле реализации */

extern NSString *const kFirstNameKey;

extern NSString *const kLastNameKey;


@interface Person: NSObject


@property (nonatomic, copy) NSString *firstName;

@property (nonatomic, copy) NSString *lastName;


— (id) objectForKeyedSubscript:(id<NSCopying>)paramKey;

— (void) setObject:(id)paramObject forKeyedSubscript:(id<NSCopying>)paramKey;


@end


Метод objectForKeyedSubscript: будет вызываться в вашем классе всякий раз, когда программист предоставит ключ и захочет прочитать в вашем классе значение, соответствующее данному ключу. Очевидно, тот параметр, который будет вам передан, будет представлять собой ключ, по которому программист хочет считать интересующее его значение. Дополнительно к этому методу мы будем вызывать в нашем классе метод setObject: forKeyedSubscript: всякий раз, когда программист захочет задать значение для конкретного ключа. Итак, в данной реализации мы хотим проверить, ассоциированы ли заданные ключи с именами и фамилиями. Если это так, то собираемся установить/получить в нашем классе значения имени и фамилии:


#import «Person.h»


NSString *const kFirstNameKey = @"firstName";

NSString *const kLastNameKey = @"lastName";


@implementation Person


— (id) objectForKeyedSubscript:(id<NSCopying>)paramKey{


NSObject<NSCopying> *keyAsObject = (NSObject<NSCopying> *)paramKey;

if ([keyAsObject isKindOfClass: [NSString class]]){

NSString *keyAsString = (NSString *)keyAsObject;

if ([keyAsString isEqualToString: kFirstNameKey] ||

[keyAsString isEqualToString: kLastNameKey]){

return [self valueForKey: keyAsString];

}

}


return nil;

}


— (void) setObject:(id)paramObject forKeyedSubscript:(id<NSCopying>)paramKey{

NSObject<NSCopying> *keyAsObject = (NSObject<NSCopying> *)paramKey;

if ([keyAsObject isKindOfClass: [NSString class]]){

NSString *keyAsString = (NSString *)keyAsObject;

if ([keyAsString isEqualToString: kFirstNameKey] ||

[keyAsString isEqualToString: kLastNameKey]){

[self setValue: paramObject forKey: keyAsString];

}

}

}


@end


Итак, в этом коде мы получаем ключ в методе objectForKeyedSubscript:, а в ответ должны вернуть объект, который ассоциирован в нашем экземпляре с этим ключом. Ключ, который получаем, — это объект, соответствующий протоколу NSCopying. Это означает, что при желании мы можем сделать копию такого объекта. Рассчитываем на то, что ключ будет представлять собой строку, чтобы мы могли сравнить его с готовыми ключами, которые были заранее объявлены в начале класса. В случае совпадения зададим значение данного свойства в этом классе. После этого воспользуемся методом valueForKey:, относящимся к объекту NSObject, чтобы вернуть значение, ассоциированное с заданным ключом. Но, разумеется, прежде, чем так поступить, мы должны гарантировать, что данный ключ — один из тех, которые мы ожидаем. В методе setObject: forKeyedSubscript: мы делаем совершенно противоположное — устанавливаем значения для заданного ключа, а не возвращаем их.

Теперь в любой части вашего приложения вы можете инстанцировать объект типа Person и использовать заранее определенные ключи kFirstNameKey и kLastNameKey, чтобы изменить значения свойств firstName и lastName, вот так:


Person *person = [Person new];

person[kFirstNameKey] = @"Tim";

person[kLastNameKey] = @"Cook";

__unused NSString *firstName = person[kFirstNameKey];

__unused NSString *lastName = person[kLastNameKey];

Этот код позволяет достичь точно того же результата, что и при более лобовом подходе, когда мы устанавливаем свойства класса:

Person *person = [Person new];

person.firstName = @"Tim";

person.lastName = @"Cook";

__unused NSString *firstName = person.firstName;

__unused NSString *lastName = person.lastName;


Вы также можете поддерживать и подписывание по индексу — точно как при работе с массивами. Как было указано ранее, это полезно делать, чтобы обеспечивать программисту доступ к объектам, выстраиваемым в классе в некоем естественном порядке. Но, кроме массивов, существует не так уж много структур данных, где целесообразно упорядочивать и нумеровать элементы, чего не скажешь о подписывании по ключу, которое применяется в самых разных структурах данных. Поэтому пример, которым иллюстрируется подписывание по индексу, немного надуман. В предыдущем примере у нас существовал класс Person с именем и фамилией. Теперь мы хотим предоставить программистам возможность считывать имя, указывая индекс 0, а фамилию — указывая индекс 1. Все, что требуется сделать для этого, — объявить методы objectAtIndexedSubscript: и setObject: atIndexedSubscript: в заголовочном файле класса, а затем написать реализацию. Вот как мы объявляем два этих метода в заголовочном файле класса Person:

— (id) objectAtIndexedSubscript:(NSUInteger)paramIndex;

— (void) setObject:(id)paramObject atIndexedSubscript:(NSUInteger)paramIndex;

Реализация также довольно проста. Мы берем индекс и оперируем им так, как это требуется в нашем классе. Ранее мы решили, что у имени должен быть индекс 0, а у фамилии — индекс 1. Итак, получаем индекс 0 для задания значения, присваиваем значение имени первому входящему объекту и т. д.:


— (id) objectAtIndexedSubscript:(NSUInteger)paramIndex{


switch (paramIndex){

case 0:{

return self.firstName;

break;

}

case 1:{

return self.lastName;

break;

}

default:{

[NSException raise:@"Invalid index" format: nil];

}

}


return nil;

}


— (void) setObject:(id)paramObject atIndexedSubscript:(NSUInteger)paramIndex{

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*