在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函数。可能是因为对这些变量的操作,本身就是原子性的。

iOS中atomic修饰符的底层实现的更多相关文章

  1. JAVA语言中的修饰符

    JAVA语言中的修饰符 -----------------------------------------------01--------------------------------------- ...

  2. Java中的 修饰符

    java中的修饰符分为类修饰符,字段修饰符,方法修饰符. 根据功能的不同,主要分为以下几种. 1.权限访问修饰符  访问权限的控制常被称为具体实现的隐藏 把数据和方法包进类中,以及具体实现的隐藏,常共 ...

  3. C/C++ 中 const 修饰符用法总结

    C/C++ 中 const 修饰符用法总结 在这篇文章中,我总结了一些C/C++语言中的 const 修饰符的常见用法,供大家参考. const 的用法,也是技术性面试中常见的基础问题,希望能够帮大家 ...

  4. Java中final修饰符深入研究

    一.开篇 本博客来自:http://www.cnblogs.com/yuananyun/ final修饰符是Java中比较简单常用的修饰符,同时也是一个被"误解"较多的修饰符.对很 ...

  5. vue中的修饰符

    Vue2.0学习笔记:Vue事件修饰符的使用   事件处理 如果需要在内联语句处理器中访问原生DOM事件.可以使用特殊变量$event,把它传入到methods中的方法中. 在Vue中,事件修饰符处理 ...

  6. Java中各种修饰符与访问修饰符

    Java中各种修饰符与访问修饰符 类: 访问修饰符 修饰符 class 类名称 extends 父类名称 implement 接口名称 (访问修饰符与修饰符的位置可以互换) 访问修饰符 名称 说明 备 ...

  7. Java中访问修饰符public、private、protecte、default

    Java中访问修饰符public.private.protecte.default的意义讲解:public: Java语言中访问限制最宽的修饰符,一般称之为“公共的”.被其修饰的类.属性以及方法不 仅 ...

  8. java中的修饰符和基本数据类型

    1.java中的修饰符 java中的修饰符主要是用来对类资源进行一个权限控制,上面表格表现的很清晰,无需多言. 2.java中的基本数据类型 java中的数据类型分为引用类型和基本类型.基本数据类型有 ...

  9. 对于Java中权限修饰符的理解

    老是把Java中权限修饰符给忘记,写一个博客加深印象吧 权限分为四个作用域:当前类,同一个包,其他包的子类,其他包的类. 首先要知道包的概念,Java中一个包是指一个package下的所有文件. pr ...

  10. 关于Java语言中那些修饰符

    一.在java中提供的一些修饰符,这些修饰符可以修饰类.变量和方法,在java中常见的修饰符有:abstract(抽象的).static(静态的).public(公共的).protected(受保护的 ...

随机推荐

  1. KingbaseES Collate排序规则对结果集的影响

    背景 前端在客户现场遇到一个问题,模糊查询报错:error:invalid multibyte charactor for locale pg the server LC_TYPE locale is ...

  2. Java实现软件设计模式---抽象工厂模式(性别产品等级结构肤色产品族)

    一.题目要求 二.画出对应的类图 三.文件目录结构 四.具体实现代码 Black.java 1 package com.a004; 2 3 public class Black implements ...

  3. 动图演示步骤 Vmware安装Centos-7 最小安装/图形化界面及常见错误参考,基础配置推荐

    程序软件工具安装篇 --[Linux](Vmware/Centos-7) 目录 程序软件工具安装篇 --[Linux](Vmware/Centos-7) ①:文件准备工作 虚拟机工具安装文件 系统镜像 ...

  4. #欧拉序,线段树#洛谷 6845 [CEOI2019] Dynamic Diameter

    题目 动态修改边权,强制在线询问树的直径. 分析 设 \(dis[x]\) 表示 \(x\) 到1号点的距离. 那么树的直径就可以表示成 \(dis[x]+dis[y]-2*dis[lca]\) 只需 ...

  5. #树状数组,线段树,离散#JZOJ 3854 分组

    题目 Bsny所在的精灵社区有\(n\)个居民,每个居民有一定的地位和年龄,\(r_i\)表示第\(i\)个人的地位,\(a_i\)表示第\(i\)个人的年龄. 最近社区里要举行活动,要求几个人分成一 ...

  6. HUAWEI DevEco Testing注入攻击测试:以攻为守,守护OpenHarmony终端安全

    OpenAtom OpenHarmony(以下简称"OpenHarmony")作为面向全场景的开源分布式操作系统,可广泛应用于智能家居物联网终端.智能穿戴.智慧大屏.汽车智能座舱. ...

  7. RabbitMQ 06 工作队列模式

    工作队列模式结构图: 这种模式非常适合多个工人等待任务到来的场景.任务有多个,一个一个丢进消息队列,工人也有很多个,就可以将这些任务分配个各个工人,让他们各自负责一些任务,并且做的快的工人还可以多完成 ...

  8. Java实现打包压缩文件或文件夹生成zip以实现多文件批量下载

    有时候在系统中需要一次性下载多个文件,但逐个下载文件比较麻烦.这时候,最好的解决办法是将所有文件打包成一个压缩文件,然后下载这个压缩文件,这样就可以一次性获取所有所需的文件了. 下面是一个名为Comp ...

  9. 抓包整理————wireshark DNS 演练[三]

    前言 简单整理一些wireshark dns 演练. 正文 简单介绍一下什么是dns. 这个其实可以百度一下哈,其实就是将域名转换为ip. 然后查询过程是这样的: 根域名服务器 权威服务器 dns 被 ...

  10. 重新点亮shell————周期性脚本[八]

    前言 简单介绍一下周期性脚本 正文 周期性脚本之前先介绍一下信号. 捕获信号脚本的编写: kill 默认会发送15号信号给应用程序 ctrl+c 发送2号信号给应用程序 9号信号不可阻塞信号 所以只有 ...