50

一个快速的问题。

如果我有一个属性和一个用相同名称声明的 ivar:

在 .h 文件中:

(Reminder*)reminder;
@property(nonatomic,strong)(Reminder*)reminder;

在 .m 文件中,如果我使用 ARC,我应该使用 ivar 还是 init 方法中的属性?

- (id)initWithReminder:(Reminder*)reminder_ {
    self = [super init];
    if (self) {
        reminder = reminder_;
    }
    return self;
}

或者我应该使用该属性来获得自动引用计数的好处,如下所示:

- (id)initWithReminder:(Reminder*)reminder_ {
    self = [super init];
    if (self) {
        self.reminder = reminder_;
    }
    return self;
}

我不确定在对象初始化的哪个时间点可以使用点符号访问属性。

4

3 回答 3

68

在部分构造的状态下使用直接访问,不管 ARC:

- (id)initWithReminder:(Reminder*)reminder_ {
    self = [super init];
    if (self) {
        reminder = reminder_;
        // OR
        reminder = [reminder_ retain];
    }
    return self;
}

这是因为self.whatever会触发其他副作用,例如键值观察 (KVO) 通知,或者您的类实现(显式)或子类覆盖setWhatever:——这可能会将您部分初始化的实例暴露给其他 API(包括它自己的) ,它正确地假设他们正在处理一个完全构造的对象。

可以手动验证一个类是否能够在部分初始化的状态下运行,但这需要大量维护并且(坦率地说)当其他人想要对您的类进行子类化时是不切实际或不可能的。它需要大量时间和维护,这样做并没有实质性的好处,特别是如果您尝试使用该方法作为惯例。

所以保证正确性的统一方式是在部分构造状态下使用直接访问,避免使用访问器。

注意:我使用的是“部分构造”,因为初始化只是图片的一半;-dealloc有类似的警告。

关于为什么应该在部分构造状态(ARC || MRC)中使用直接访问的更多细节可以在这里找到:Initializing a property, dot notation

于 2011-11-08T19:50:57.990 回答
5

不,你不应该!

你可以在这里找到说明为什么
苹果建议不要这样做。在这里阅读

于 2014-03-20T08:49:09.653 回答
0

我不确定在对象初始化的哪个时间点可以使用点符号访问属性。

由于点表示法仍然是一种 Objective-C 方法(实际上是 ObjC 方法下的 C 方法),因此点表示法或调用该方法是完全安全的,因为该方法已准备好处理内存中的底层类型无论他们碰巧处于什么状态。关于避免使用未初始化(可能)车库内存段的正常规则仍然适用。这是在 init 中使用 ivar 的最强动机。

但是,如果您的方法 (getter|setter) 能够正确使用内存段——无论它是否在被读取之前先被写入——那么一定要在 init 方法中使用你的 getter。Lazy getter 利用它将初始化的指针以“nil”开始的假设来决定执行初始化。如果您不能假设您的内存的初始内容,那么初始化 ivar 可能是最安全的过程。

如果方法能够在这种情况下正确运行,为什么在 init 中永远不要使用 setter 或 getter 的规则?

于 2015-10-30T21:04:45.020 回答