• iOS中atomic修饰符的底层实现


    在iOS中,atomic表示一个类的属性getter/setter具有原子性。那么iOS底层是如何保证这种原子性的呢?

    我们有一个类A,它有一个属性X具有atomic:

    @property (atomic, strong) X *x;

    当我们为这个这个X属性赋值的时候:-[A setX:]的汇编代码如下:

     1 0x10e320f00 <+0>:  push   rbp
     2     0x10e320f01 <+1>:  mov    rbp, rsp
     3     0x10e320f04 <+4>:  sub    rsp, 0x20
     4     0x10e320f08 <+8>:  mov    qword ptr [rbp - 0x8], rdi
     5     0x10e320f0c <+12>: mov    qword ptr [rbp - 0x10], rsi
     6     0x10e320f10 <+16>: mov    qword ptr [rbp - 0x18], rdx
     7     0x10e320f14 <+20>: mov    rsi, qword ptr [rbp - 0x10]
     8     0x10e320f18 <+24>: mov    rax, qword ptr [rbp - 0x8]
     9     0x10e320f1c <+28>: mov    rcx, qword ptr [rbp - 0x18]
    10     0x10e320f20 <+32>: mov    rdi, rax
    11     0x10e320f23 <+35>: mov    rdx, rcx
    12     0x10e320f26 <+38>: mov    ecx, 0x18
    13     0x10e320f2b <+43>: call   0x10e321312               ; symbol stub for: objc_setProperty_atomic 注意这行
    14     0x10e320f30 <+48>: add    rsp, 0x20
    15     0x10e320f34 <+52>: pop    rbp
    16     0x10e320f35 <+53>: ret    

    上面的汇编代码第13行显示,setX里面调用了objc rumtime函数objc_setProperty_atomic函数。打开objc_setProperty_atomic源码:

    void objc_setProperty_atomic(id self, SEL _cmd, id newValue, ptrdiff_t offset)
    {
        reallySetProperty(self, _cmd, newValue, offset, true, false, false);
    }
     1 static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
     2 {
     3     if (offset == 0) {
     4         object_setClass(self, newValue);
     5         return;
     6     }
     7 
     8     id oldValue;
     9     id *slot = (id*) ((char*)self + offset);
    10 
    11     if (copy) {
    12         newValue = [newValue copyWithZone:NULL];
    13     } else if (mutableCopy) {
    14         newValue = [newValue mutableCopyWithZone:NULL];
    15     } else {
    16         if (*slot == newValue) return;
    17         newValue = objc_retain(newValue);
    18     }
    19 
    20     if (!atomic) {
    21         oldValue = *slot;
    22         *slot = newValue;
    23     } else { //如果是atomic属性修饰,会加上自旋锁
    24         spin_lock_t *slotlock = &PropertyLocks[GOODHASH(slot)];
    25         _spin_lock(slotlock);
    26         oldValue = *slot;
    27         *slot = newValue;        
    28         _spin_unlock(slotlock);
    29     }
    30 
    31     objc_release(oldValue);
    32 }

    通过源码发现,objc_setProperty_atomic函数调用了reallySetProperty函数,而reallySetProperty函数的23行,如果是atomic属性修饰的,为这个变量赋值会加上自旋锁。

     

    下面我们看下访问X这个变量时的getter方法:-[A x],汇编代码如下:

     1 ->  0x10c09fed0 <+0>:  push   rbp
     2     0x10c09fed1 <+1>:  mov    rbp, rsp
     3     0x10c09fed4 <+4>:  mov    qword ptr [rbp - 0x8], rdi
     4     0x10c09fed8 <+8>:  mov    qword ptr [rbp - 0x10], rsi
     5     0x10c09fedc <+12>: mov    rsi, qword ptr [rbp - 0x10]
     6     0x10c09fee0 <+16>: mov    rdi, qword ptr [rbp - 0x8]
     7     0x10c09fee4 <+20>: mov    edx, 0x18
     8     0x10c09fee9 <+25>: mov    ecx, 0x1
     9     0x10c09feee <+30>: pop    rbp
    10     0x10c09feef <+31>: jmp    0x10c0a02fa               ; symbol stub for: objc_getProperty 寄存器ecx为1,表示传递给objc_getProperty的第4个参数为YES,而这个参数刚好表示是否是atomic的

    上面汇编代码第10行,getter方法访问了objc运行时的objc_getProperty方法,我们查看源码:

    1 id 
    2 objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) 
    3 {
    4     return objc_getProperty_non_gc(self, _cmd, offset, atomic);
    5 }
     1 id objc_getProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
     2     if (offset == 0) {
     3         return object_getClass(self);
     4     }
     5 
     6     // Retain release world
     7     id *slot = (id*) ((char*)self + offset);
     8     if (!atomic) return *slot;
     9         
    10     // Atomic retain release world    
    11     // Atomic属性访问会加锁
    12     spin_lock_t *slotlock = &PropertyLocks[GOODHASH(slot)];
    13     _spin_lock(slotlock);
    14     id value = objc_retain(*slot);
    15     _spin_unlock(slotlock);
    16     
    17     // for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
    18     return objc_autoreleaseReturnValue(value);
    19 }

    从上面getter方法汇编码可以知道传递给objc_getProperty第四个参数为YES,也就是atomic参数为YES。而objc_getProperty调用了objc_getProperty_non_gc函数,这个函数里面第12行,如果是atomic属性,访问的时候会加锁。

     

    测试的时候发现,如果atomic修饰的是诸如整型这些简单类型的变量,setter/getter时都是使用对象首地址+偏移量完成,而不会调用objc_getProperty函数和objc_setProperty_atomic函数。可能是因为对这些变量的操作,本身就是原子性的。

  • 相关阅读:
    两小时后执行打开浏览器的操作
    Markdown语法
    Python机器学习2.2
    身份证姓名与身份证号校验
    根据银行卡号获取开户行
    生成微信小程序二维码
    .net 生成二维码
    微信支付API V3(.Net Core)
    .net 生成项目xml描述文件
    汉字排序问题
  • 原文地址:https://www.cnblogs.com/chaoguo1234/p/13286037.html
Copyright © 2020-2023  润新知