Главная » iPhone, Objective-C, Программирование для iOS и MacOS » Подробнее о копировании Objective-C

0

Большинство классов Objective-C вообще не содержит метода copyWithZone:. Программисты Objective-C создают меньше копий, чем кажется на первый взгляд. Интересно, что методы сору и mutаblеСору определяются в NSObject следующим образом:

-­‐  (id)copy {

return [self copyWithZone:NULL];

}

-­‐     (id)mutableCopy

{

return [self mutableCopyWithZone:NULL];

}

Следовательно, при попытке выполнения кода

Appliance *b = [[Appliance alloc] init]; Appliance *c = [b copy];

вы получите ошибку следующего вида:

-­‐[Appliance copyWithZone:]: unrecognized selector sent to instance 0x100110130

Атомарность

Эта книга начального уровня, а атрибут atomic/nonatomic относится к относительно нетривиальной теме многопоточности. Сейчас вам необходимо знать следующее: в режиме nonatomic ваш sеt-метод будет выполняться чуть быстрее. Заглянув в заголовки UlКit, вы увидите, что все свойства помечены атрибутом nonatomic. Вам тоже "Стоит объявлять свои свойства с атрибутом nonatomic.

(Я даю этот совет всем без исключения. Однако в каждой группе находится умник, кто знает ровно  столько, чтобы возразить.  Он говорит: «Ho ведь в многопоточном приложении мне потребуется защита, которую мне обеспечат атомарные sеt-методы». И мне стоило бы сказать: «Не думаю, что вы скоро будете писать многопоточный код. А когда начнете, атомарность sеt-методов вам вряд ли поможет». Но я говорю другое: «Ладно, тогда оставьте свои sеt-методы атомарными». Потому что не стоит говорить то, что люди еще не готовы услышать.)

В Appliance.h объявите свои методы доступа с атрибутом nonatomic:

@property (copy, nonatomic) NSString *productName;

@property (nonatomic) int voltage;

К сожалению, на момент написания книги значение atomic используется по умолчанию, поэтому вам придется внести это изменение.

Запись «ключ-значение»

Запись «ключ-значение» позволяет читать и задавать свойства по имени. Соответствующие методы определены в NSObject, поэтому каждый объект поддерживает данную возможность.

Откройте файл main.m и найдите строку:

[a setProductName:@"Washing Machine"];

Перепишите ее с использованием записи «ключ-значение»:

[a setValue:@"Washing Machine" forKey:@"productName"];

В этом случае метод setValue:forKey:, определенный в NSObject, будет искать sеt-метод С именем setProductName:. Если у объекта нет метода setProductName:, он обращается к переменной экземпляра напрямую.

Запись  «ключ-значение»  также  позволяет  прочитать  значение  переменной.

Включите в main.m строку для вывода имени устройства:

int main (int argc, const char * argv[])

{

@autorelease {

Appliance *a = [[Appliance alloc] init]; NSLog(@"a is %@", a);

[a setValue:@"Washing Machine" forKey:@"productName"]; [a setVoltage:240];

NSLog(@"a is %@", a);

NSLog(@"the product name is %@", [a valueForKey:@"productName"]);

}

return 0;

}

На этот раз метод valueForKey:, определенный в NSObject, ищет метод доступа с именем productName. Если метод productName не найден, он обращается к переменной экземпляра напрямую.

Если  имя  свойства  указано  неверно,  вы  не  получите  предупреждения  от

компилятора, но во время выполнения произойдет ошибка. Сделайте ошибку в main.m:

NSLog(@"the product name is %@", [a valueForKey:@"productNammmme"]);

Постройте и запустите программу – вы получите следующее сообщение об ошибке:

*** Terminating app due to uncaught exception ‘NSUnknownKeyException’, reason: ‘[<Appliance 0x100108dd0> valueForUndefinedKey:]:

this class is not key value coding-­‐compliant for the key productNammmme.’

Исправьте ошибку, прежде чем продолжать.

Почему запись .ключ-значение~ представляет интерес? Каждый раз, когда стандартная библиотека записывает данные в ваши объекты, она использует setValue: forKey:. Каждый раз, когда стандартная библиотека читает данные из ваших объектов, она использует valueForKey:. Например, библиотека СоrеData упрощает сохранение объектов в базе данных SQLite и их последующую загрузку. Для работы с пользовательским объектами, содержащими данные, используется запись «ключ- значение».

Чтобы убедиться в том, что запись «ключ-значение» выполнит операции с

переменными даже при отсутствии методов доступа, закомментируйте объявление

@рrореrtу для свойства productName в файле Appliance.h:

#import <Foundation/Foundation.h>

@interface Appliance : NSObject { NSString *productName;

int voltage;

}

// @property (copy) NSString *productName; @property (nonatomic) int voltage;

// основной инциализатор

-­‐    (id)initWithProductName:(NSString   *)pn;

@end

Также   удалите   все   случаи   использования   методов   setProductName:   и

ргоductName из Appliance.m:

@implementation Appliance

@synthesize voltage;

-­‐    (id)initWithProductName:(NSString   *)pn

{

self = [super init]; if (self) {

productName = [pn copy];

[self setVoltage:120];

}

return self;

}

-­‐  (id)init {

return [self initWithProductName:@"Unknown"];

}

-­‐   (NSString  *)description

{

}

@end

return [NSString stringWithFormat:@"<%@: %d volts>", productName, voltage];

Постройте и запустите программу. Несмотря на отсутствие методов доступа для productName, значение переменной все равно может читать и задавать из других методов. Это является очевидным нарушением идеи инкапсуляции объекта – методы объекта открыты, но переменные экземпляров должны оставаться приватными, то

есть  недоступными  извне.  Если  бы  запись  «ключ-значение»  не  была  невероятно полезной, никто бы с этим не смирился.

Не-объектные типы

Методы записи «ключ-значение» предназначены для работы с объектами, но в некоторых свойствах хранятся не-объектные типы (например, int или float – скажем, voltage относится к типу int). Как задать значение voltage в записи «ключ- значение»? Используйте NSNumber.

В файле main.m измените строку, задающую значение voltage:

[а setVoltage:240];

следующей строкой:

[a setValue:[NSNumber numberWithInt:240] forKey:@"voltage"];

Добавьте в Appliance.m метод доступа для вывода информации о вызове:

-­‐       (void)setVoltage:(int)x

{

NSLog(@"setting voltage to %d", x); voltage = x;

}

Постройте и запустите программу

Аналогичным  образом  по  запросу  valueForKey:@"voltage" вы  получите объект NSNumber, содержащий значение voltage.

Источник: Аарон Хилегас, «Objective-C. Программирование для iOS и MacOS», 2012 г.

По теме:

  • Комментарии