这是一个小细节,但是每次我懒洋洋地加载东西都会陷入困境。这两种方法都可以接受吗?要么更好吗?假设该变量具有保留属性。

方法#1

(AnObject *)theObject{
    if (theObject == nil){
        theObject = [[AnObject createAnAutoreleasedObject] retain];
    }
    return theObject;
}

方法#2

(AnObject *)theObject{
    if (theObject == nil){
        self.theObject = [AnObject createAnAutoreleasedObject];
    }
    return theObject;
}

首先,我不确定是否可以访问登录器中的另一个访问器功能(尽管不明白为什么不)。但是,如果设置器做一些特别的事情(或者,如果将属性更改为保留且未检查getter,则似乎不经过设置器而不经过设置器的设置似乎同样不好。

有帮助吗?

解决方案

实际上,两者都非常脆弱,完全没有相同,具体取决于班级的客户在做什么。使它们相同非常容易 - 见下文 - 但是使其脆弱更加困难。这就是懒惰初始化的价格(以及为什么我通常试图以这种方式避免懒惰初始化,而宁愿将子系统的初始化视为整体应用状态管理的一部分)。

使用#1,您避免了二传手,因此,观察变化的任何内容都不会看到更改。通过“观察”,我专门指的是键值观察(包括使用KVO自动更新UI的可可绑定)。

使用#2,您将触发更改通知,更新UI,否则就完全像调用了设置器一样。

在这两种情况下,如果对象的初始化称为Getter,则您都有无限递归的潜力。其中包括任何观察者要求旧值作为变更通知的一部分。不要那样做。

如果要使用两种方法,请仔细考虑后果。一个人有可能将应用程序留在一个不一致的状态下,因为财产的变化没有通知,另一个财产有可能发生僵局。

最好完全避免问题。见下文。


考虑(垃圾收集,标准可可命令行工具:

#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 在观察选项列表中,它确实挂起了。

回到我之前发表的评论;最好的解决方案是 不要将懒惰的初始化作为getter/setter的一部分. 。它太细粒度了。您会更好地管理对象图状态,并且作为其中的一部分,它的状态过渡基本上是“ Yo!我现在要使用此子系统!温暖那个坏男孩! “这可以做到懒惰的初始化。

其他提示

这些方法永远不会相同。第一个是对的,而第二个是 错误的呢getter可能永远不会打电话 will/didChangeValueForKey: 因此,也不是设定器。如果观察到该特性,这将导致无限递归。

此外,何时初始化成员时,没有任何状态更改可观察。您向您的对象询问 theObject 你明白了。创建时,是一个实施细节,对外界无关。

如果您知道属性设置器方法是标准固定设置器,则它们相同。如果没有,您需要确定在该操作期间是否应调用二传手的其他行为。如果您不知道,它最安全的使用设置器,因为它的行为可能很重要。不要流汗。

两者基本上都是相同的,实际上它仅仅由您选择哪种最适合您的情况。您已经真正描述了有关使用属性语法的利弊。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top