从另一个线程调用setter时读取实例变量是否是线程安全的?

时间:2016-11-09 17:05:56

标签: objective-c thread-safety

我有一个属性对象:

@interface Car
@property(strong) NSLicensePlate *licensePlate;
@end

我在方法中使用该属性:

- (void) doSomething {
    [_licensePlate frobnicate];
}

可以用另一种方法改变属性值:

- (void) doSomethingElse {
    [self setLicensePlate:[_licensePlateDealer cleanLicensePlate]];
}

现在,如果在我使用实例变量访问牌照属性时从另一个线程调用-doSomethingElse方法(如-doSomething方法中所示),是否可以获得段错误?

-setLicensePlate setter是否有可能在我调用_licensePlate之前和分配新的有效值之前释放-frobnicate中存储的值?是否有助于致电[self licensePlate]而不是直接使用_licensePlate

2 个答案:

答案 0 :(得分:3)

如果您想要享受此属性的atomic行为(因为您未指定nonatomic限定符而获得的默认行为),则必须使用getter({{ 1}}或self.licensePlate),不要使用ivar([self licensePlate])。

一般来说,除了(a)_licensePlate方法之外,在任何地方使用getter和setter通常都是谨慎的。 (b)和自定义存取方法。开销可以忽略不计,你可以避免一些潜在问题,包括原子性,内存语义,KVO,面向未来的代码,如果你在将来某个日期定制访问器方法等等。

但是,假设您只通过访问器方法(getter和setter)访问您的属性,Programming with Objective-C: Encapsulating Data描述的init限定符确保您正在检索的指针本身/设置不会被另一个线程破坏:

  

[Atomic]意味着合成访问器确保始终通过getter方法完全检索值或通过setter方法完全设置,即使从不同的线程同时调用访问器也是如此。

在回答您的问题时,如果另一个线程更改atomic属性,而licensePlate方法在另一个线程上运行,则该方法返回之前不会释放该原始对象。 / p>

但要明确的是,frobnicate限定符确保线程安全。正如上面的指南继续警告我们:

  

注意:属性原子性与对象的线程安全性不是同义词。

     

考虑一个atomic对象,其中使用来自一个线程的原子访问器来更改一个人的名字和姓氏。如果另一个线程同时访问这两个名称,则原子getter方法将返回完整的字符串(不会崩溃),但不能保证这些值将是相对于彼此的正确名称。如果在更改之前访问了第一个名称,但在更改后访问了姓氏,则最终会出现一组不一致,不匹配的名称。

     

这个例子非常简单,但是当在相关对象的网络中考虑时,线程安全问题变得更加复杂。 Concurrency Programming Guide中详细介绍了线程安全性。

因此,可能在一个线程上使用XYZPerson同时在另一个线程上执行其他操作时是线程安全的,但它也可能不是。这取决于使用此牌照对象可以完成的所有不同事情。由于frobnicate提供的保护是如此简约,我们经常会采用一些同步(通过GCD串行队列或GCD读写器模式,或通过Threading Programming Guide: Synchronization中概述的任何同步方法,如锁)协调来自不同线程的交互。

答案 1 :(得分:0)

定义属性时,可以将其设置为atomic(默认值)或nonatomic

由于您使用的是atomic默认设置,因此您应该对线程安全做得很好,但这也取决于您实施frobnicatesetLicensePlate:和{{1}的方式}。

请参阅此问题以获取有关cleanLicensePlateatomic的更多详细信息:What's the difference between the atomic and nonatomic attributes?