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

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

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


当前回答

原子=螺纹安全

非原子=无线程安全

螺纹安全:

如果实例变量在从多个线程访问时行为正确,则它们是线程安全的,而不考虑运行时环境对这些线程执行的调度或交织,并且调用代码部分没有额外的同步或其他协调。

在我们的背景下:

如果一个线程更改了实例的值,则所有线程都可以使用更改的值,并且一次只有一个线程可以更改该值。

在何处使用原子:

如果要在多线程环境中访问实例变量。

原子的含义:

没有非原子的速度快,因为非原子的运行时不需要任何看门狗的工作。

在何处使用非原子:

如果实例变量不会被多个线程更改,您可以使用它。它可以提高性能。

其他回答

没有这样的关键字“atomic”

@property(atomic, retain) UITextField *userName;

我们可以使用上面的

@property(retain) UITextField *userName;

请参阅堆栈溢出问题,如果我使用@property(atomic,retain)NSString*myString,我会遇到问题。

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

 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);
    }

最后两个是相同的;“atomic”是默认行为(请注意,它实际上不是一个关键字;它仅在不存在非atomic的情况下被指定——在最近版本的llvm/claung中,atomic被添加为关键字)。

假设您正在@合成方法实现,原子与非原子将更改生成的代码。如果您正在编写自己的setter/getter,原子/非原子/保留/分配/复制只是建议。(注意:@synthey现在是LLVM最新版本中的默认行为。也不需要声明实例变量;它们也会自动合成,并且在名称前加一个_,以防止意外的直接访问)。

使用“atomic”,合成的setter/getter将确保始终从getter返回或由setter设置整个值,而不管setter在任何其他线程上的活动如何。也就是说,如果线程A位于getter的中间,而线程B调用setter,则实际可行的值(很可能是自动释放的对象)将返回给A中的调用方。

在非原子中,没有这样的保证。因此,非原子比“原子”快得多。

“原子”不做的是保证线程安全。如果线程A同时调用getter,而线程B和C使用不同的值调用setter,那么线程A可能会得到返回的三个值中的任何一个值——在调用setter之前的值,或者在B和C中传递给setter的值。同样,对象可能会以B或C的值结束,这是无法分辨的。

确保数据完整性——多线程编程的主要挑战之一——是通过其他方式实现的。

添加到此:

当使用多个依赖财产时,单个属性的原子性也不能保证线程安全。

考虑:

 @property(atomic, copy) NSString *firstName;
 @property(atomic, copy) NSString *lastName;
 @property(readonly, atomic, copy) NSString *fullName;

在这种情况下,线程A可以通过调用setFirstName:然后调用setLastName:来重命名对象。同时,线程B可以在线程A的两次调用之间调用fullName,并将接收新的名字和旧的姓氏。

要解决这个问题,您需要一个事务模型。也就是说,某些其他类型的同步和/或排除允许在更新依赖财产时排除对fullName的访问。

在阅读了这么多文章、StackOverflow文章和制作用于检查变量属性的演示应用程序后,我决定将所有属性信息放在一起:

atomic//默认非原子的strong=保留//默认弱=未染色保持分配//默认值未染色的复制只读的readwrite//默认

在iOS中的可变属性属性或修饰符一文中,您可以找到上述所有属性,这肯定会对您有所帮助。

原子的原子意味着只有一个线程访问变量(静态类型)。atomic是线程安全的。但它的性能很慢原子是默认行为非垃圾收集环境中的原子访问器(即,当使用retain/release/autorelease时)将使用锁来确保另一个线程不会干扰值的正确设置/获取。它实际上不是一个关键字。例子:@属性(retain)NSString*name;@综合名称;非原子的非原子意味着多线程访问变量(动态类型)。非原子是线程不安全的。但它的性能很快非原子不是默认行为。我们需要在属性属性中添加非原子关键字。当两个不同的进程(线程)同时访问同一变量时,可能会导致意外行为。例子:@属性(非原子,保留)NSString*name;@综合名称;

了解差异的最佳方法是使用以下示例。

假设有一个名为“name”的原子字符串属性,如果您从线程A调用[self-setName:@“A”],从线程B调用[selfsetName:@“B”],并从线程C调用[self name],那么不同线程上的所有操作都将串行执行,这意味着如果一个线程正在执行setter或getter,那么其他线程将等待。

这使得属性“name”读/写安全,但如果另一个线程D同时调用[namerelease],那么这个操作可能会产生崩溃,因为这里没有setter/getter调用。这意味着一个对象是读/写安全的(ATOMIC),但不是线程安全的,因为另一个线程可以同时向该对象发送任何类型的消息。开发人员应确保此类对象的线程安全。

如果属性“name”是非原子的,那么上面示例中的所有线程(A、B、C和D)将同时执行,产生任何不可预测的结果。在原子的情况下,A、B或C中的任何一个将首先执行,但D仍然可以并行执行。