在属性声明中,原子和非原子意味着什么?

@property(nonatomic, retain) UITextField *userName;
@property(atomic, retain) UITextField *userName;
@property(retain) UITextField *userName;

这三者之间的操作差异是什么?


当前回答

事实是,他们使用自旋锁来实现原子属性。代码如下:

 static inline void reallySetProperty(id self, SEL _cmd, id newValue, 
      ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) 
    {
        id oldValue;
        id *slot = (id*) ((char*)self + offset);

        if (copy) {
            newValue = [newValue copyWithZone:NULL];
        } else if (mutableCopy) {
            newValue = [newValue mutableCopyWithZone:NULL];
        } else {
            if (*slot == newValue) return;
            newValue = objc_retain(newValue);
        }

        if (!atomic) {
            oldValue = *slot;
            *slot = newValue;
        } else {
            spin_lock_t *slotlock = &PropertyLocks[GOODHASH(slot)];
            _spin_lock(slotlock);
            oldValue = *slot;
            *slot = newValue;        
            _spin_unlock(slotlock);
        }

        objc_release(oldValue);
    }

其他回答

为了简化整个混淆,让我们了解互斥锁。

互斥锁,根据名称,锁定对象的可变性。因此,如果对象由一个类访问,则其他任何类都不能访问同一对象。

在iOS中,@synchronous还提供互斥锁。现在它以FIFO模式运行,并确保流不受共享同一实例的两个类的影响。然而,如果任务位于主线程上,请避免使用原子财产访问对象,因为它可能会保留UI并降低性能。

Atomic是线程安全的,它很慢,而且它很好地保证(不保证)无论有多少线程试图访问同一区域,都只提供锁定值。使用atomic时,在该函数中编写的一段代码成为关键部分的一部分,一次只能执行一个线程。

它只保证螺纹安全;它不能保证这一点。我的意思是,你们为你们的汽车雇佣了一名专家司机,但这并不能保证汽车不会发生事故。然而,可能性仍然微乎其微。

原子——它无法分解,所以结果是意料之中的。使用非原子-当另一个线程访问内存区域时,它可以修改它,因此结果是意外的。

代码对话:

原子使属性线程的getter和setter安全。例如,如果你写了:

self.myProperty = value;

是线程安全的。

[myArray addObject:@"Abc"] 

不是线程安全的。

苹果的文档中对此进行了解释,但下面是一些实际发生情况的示例。

请注意,没有“atomic”关键字,如果不指定“nonatomic(非原子)”,则属性是原子的,但显式指定“atomic”将导致错误。

如果不指定“非原子”,则该属性是原子的,但如果需要,在最近的版本中仍然可以显式指定“原子”。

//@property(nonatomic, retain) UITextField *userName;
//Generates roughly

- (UITextField *) userName {
    return userName;
}

- (void) setUserName:(UITextField *)userName_ {
    [userName_ retain];
    [userName release];
    userName = userName_;
}

现在,原子变体有点复杂:

//@property(retain) UITextField *userName;
//Generates roughly

- (UITextField *) userName {
    UITextField *retval = nil;
    @synchronized(self) {
        retval = [[userName retain] autorelease];
    }
    return retval;
}

- (void) setUserName:(UITextField *)userName_ {
    @synchronized(self) {
      [userName_ retain];
      [userName release];
      userName = userName_;
    }
}

基本上,原子版本必须使用锁以保证线程安全,并且还会碰撞对象上的引用计数(以及自动释放计数以平衡它),从而保证该对象对于调用者存在,否则如果另一个线程正在设置该值,则存在潜在的争用条件,导致引用计数降至0。

实际上,根据财产是标量值还是对象,以及保留、复制、只读、非原子等交互方式,这些东西的工作方式有很多不同的变体。一般来说,属性合成器只知道如何为所有组合做“正确的事情”。

原子意味着一次只能有一个线程访问变量(静态类型)。原子是线程安全的,但它很慢。

非原子意味着多个线程可以同时访问变量(动态类型)。非原子线程不安全,但速度很快。

我在这里找到了原子和非原子财产的一个很好的解释。以下是一些相关文本:

“原子”意味着它不能分解。在OS/编程术语中,原子函数调用是一个不能中断的调用——必须执行整个函数,并且在完成之前,不能通过操作系统通常的上下文切换将其从CPU中移出。万一你不知道:由于CPU一次只能做一件事,操作系统会在很小的时间段内对所有正在运行的进程轮流访问CPU,从而产生多任务的错觉。CPU调度器可以(并且确实)在进程执行的任何时间点中断进程,即使是在函数调用中间。因此,对于像更新共享计数器变量这样的操作,如果两个进程可以同时尝试更新变量,则必须“原子”地执行这些操作,即,每个更新操作必须完整完成,然后才能将任何其他进程交换到CPU上。所以我猜测,在这种情况下,原子意味着属性读取器方法不能被中断,实际上意味着该方法读取的变量不能中途更改其值,因为其他线程/调用/函数被交换到CPU上。

因为原子变量不能被中断,所以它们在任何时候包含的值(线程锁)都保证不会被破坏,尽管确保这个线程锁会使对它们的访问变慢。另一方面,非原子变量不能保证这一点,但确实提供了快速访问的奢侈。总之,当您知道变量不会被多个线程同时访问时,可以使用非原子的,这样可以加快速度。