Оба на самом деле довольно хрупкие и совсем не идентичны, в зависимости от того, что делают клиенты класса. Сделать их идентичными достаточно просто - см. Ниже - но сделать его менее хрупким сложнее. Такова цена ленивой инициализации (и почему я вообще стараюсь избегать ленивой инициализации таким образом, предпочитая лечить инициализацию подсистем в рамках общего управления государственными приложениями).
С # 1 вы избегаете настройки, и, таким образом, что-либо, наблюдающее изменение, не увидит изменения. «Наблюдая», я специально ссылаюсь на наблюдение за ключевыми значениями (включая привязки Cocoa, которые используют KVO для автоматического обновления пользовательского интерфейса).
С # 2 вы инициируете уведомление об изменении, обновляете пользовательский интерфейс и в противном случае точно так же, как если бы вызывался сеттер.
В обоих случаях у вас есть потенциал для бесконечной рекурсии, если инициализация объекта вызывает геттер. Это включает в себя, если какой-либо наблюдатель запрашивает старое значение как часть уведомления об изменении. Не делай этого.
Если вы собираетесь использовать любой из этих методов, внимательно изучите последствия. У одного есть потенциал, чтобы оставить приложение в противоречивом состоянии, поскольку изменение состояния объекта не уведомило, а другое - в тупике.
Лучше всего избежать проблемы. Смотри ниже.
Рассмотрим (сбор мусора на, стандартный инструмент командной строки какао:.
#import <Foundation/Foundation.h>
@interface Foo : NSObject
{
NSString *bar;
}
@property(nonatomic, retain) NSString *bar;
@end
@implementation Foo
- (NSString *) bar
{
if (!bar) {
NSLog(@"[%@ %@] lazy setting", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
[self willChangeValueForKey: @"bar"];
bar = @"lazy value";
[self didChangeValueForKey: @"bar"];
}
return bar;
}
- (void) setBar: (NSString *) aString
{
NSLog(@"[%@ %@] setting value %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), aString);
bar = aString;
}
@end
@interface Bar:NSObject
@end
@implementation Bar
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
{
NSLog(@"[%@ %@] %@ changed\n\tchange:%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), keyPath, change);
}
@end
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Foo *foo = [Foo new];
Bar *observer = [Bar new];
CFRetain(observer);
[foo addObserver:observer forKeyPath:@"bar"
options: NSKeyValueObservingOptionPrior | NSKeyValueObservingOptionNew
context:NULL];
foo.bar;
foo.bar = @"baz";
CFRelease(observer);
[pool drain];
return 0;
}
Это не вешает Он извергает:
2010-09-15 12:29:18.377 foobar[27795:903] [Foo bar] lazy setting
2010-09-15 12:29:18.396 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed
change:{
kind = 1;
notificationIsPrior = 1;
}
2010-09-15 12:29:18.397 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed
change:{
kind = 1;
new = "lazy value";
}
2010-09-15 12:29:18.400 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed
change:{
kind = 1;
notificationIsPrior = 1;
}
2010-09-15 12:29:18.400 foobar[27795:903] [Foo setBar:] setting value baz
2010-09-15 12:29:18.401 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed
change:{
kind = 1;
new = baz;
}
Если вы должны были добавить NSKeyValueObservingOptionOld
к списку вариантов наблюдения, это очень зависает.
Возвращение к комментарию, который я сделал ранее; наилучшим решением является не выполнять ленивую инициализацию как часть вашего геттера/сеттера. Он слишком мелкозернистый. Вам намного лучше управлять состоянием графического объекта на более высоком уровне и, как часть этого, иметь переход состояния, который в основном состоит из «Yo! Я собираюсь использовать эту подсистему сейчас! Теплый этот плохой мальчик! " что делает ленивую инициализацию.
Это правильный ответ, на мой взгляд. –
На самом деле нет изменений, которые следует соблюдать при ленивой инициализации объекта, поэтому этот ответ ** неверен. – Sven
А? Является ли оно лениво инициализированным или нет, не имеет значения. Если я установил KVO-наблюдение «объекта» и поместил 'self.theObject = ...' в приемник, то это наблюдение KV ** будет срабатывать **. «Ленивая инициализация» - это всего лишь название шаблона; ни компилятор, ни среда выполнения ничего не знают об этом. – bbum