+ (instancetype)createSark {
return [self new];
}
// caller
Sark *sark = [Sark createSark];

编译器改写成了形如下面的代码:

+ (instancetype)createSark {
id tmp = [self new];
return objc_autoreleaseReturnValue(tmp); // 代替我们调用autorelease
}
// caller
id tmp = objc_retainAutoreleasedReturnValue([Sark createSark]) // 代替我们调用retain
Sark *sark = tmp;
objc_storeStrong(&sark, nil); // 相当于代替我们调用了release

runtime使用了一些黑魔法进行了优化

Thread Local Storage

Thread Local Storage(TLS)线程局部存储,目的很简单,将一块内存作为某个线程专有的存储,以key-value的形式进行读写,比如在非arm架构下,使用pthread提供的方法实现:

void* pthread_getspecific(pthread_key_t);
int pthread_setspecific(pthread_key_t , const void *);

在返回值身上调用objc_autoreleaseReturnValue方法时,runtime将这个返回值object储存在TLS中,然后直接返回这个object(不调用autorelease);同时,在外部接收这个返回值的objc_retainAutoreleasedReturnValue里,发现TLS中正好存了这个对象,那么直接返回这个object(不调用retain)。
于是乎,调用方和被调方利用TLS做中转,很有默契的免去了对返回值的内存管理。

于是问题又来了,
只能动用更高级的黑魔法。

__builtin_return_address

假如被调方和主调方只有一边是ARC环境编译,(比如我们在ARC环境下用了非ARC编译的第三方库,或者反之),需要用到__builtin_return_address

这个内建函数原型是 char *__builtin_return_address(int level),
作用是得到函数的返回地址,参数表示层数,如__builtin_return_address(0)表示当前函数体返回地址,传1是调用这个函数的外层函数的返回值地址,以此类推。

- (int)foo {
NSLog(@"%p", __builtin_return_address(0)); // 根据这个地址能找到下面ret的地址
return 1;
}
// caller
int ret = [sark foo];
  • 函数的返回值地址,也就对应着调用者结束这次调用的地址(或者相差某个固定的偏移量,根据编译器决定)
  • 如果一个函数返回前知道调用方是ARC还是非ARC,就有机会对于不同情况做不同的处理
    ##黑魔法之反查汇编指令

通过上面的__builtin_return_address加某些偏移量,被调方可以定位到主调方在返回值后面的汇编指令:

于是乎,就有了下面的这个函数,入参是调用方__builtin_return_address传入值

static bool callerAcceptsFastAutorelease(const void * const ra0) {
const uint8_t *ra1 = (const uint8_t *)ra0;
const uint16_t *ra2;
const uint32_t *ra4 = (const uint32_t *)ra1;
const void **sym;
// 48 89 c7 movq %rax,%rdi
// e8 callq symbol
if (*ra4 != 0xe8c78948) {
return false;
}
ra1 += (long)*(const int32_t *)(ra1 + 4) + 8l;
ra2 = (const uint16_t *)ra1;
// ff 25 jmpq *symbol@DYLDMAGIC(%rip)
if (*ra2 != 0x25ff) {
return false;
}
ra1 += 6l + (long)*(const int32_t *)(ra1 + 2);
sym = (const void **)ra1;
if (*sym != objc_retainAutoreleasedReturnValue)
{
return false;
}
return true;
}

它检验了主调方在返回值之后是否紧接着调用了objc_retainAutoreleasedReturnValue,如果是,就知道了外部是ARC环境,反之就走没被优化的老逻辑。

其他Autorelease相关知识点

使用容器的block版本的枚举器时,内部会自动添加一个AutoreleasePool:

[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// 这里被一个局部@autoreleasepool包围着
}];

当然,在普通for循环和for in循环中没有,所以,还是新版的block版本枚举器更加方便。for循环中遍历产生大量autorelease变量时,就需要手加局部AutoreleasePool咯。

Autorelease返回值的快速释放机制的更多相关文章

  1. http协议中各个响应状态200_301_404_500等返回值含义快速一览

    一.定义 从HTTP的定义可以看出,HTTP协议是互联网上进行数据通信的基础协议,用来交换或传输超文本.超文本是一种结构化的文本,在包含文本的节点之间使用逻辑链接(也叫超链接). 二.概述 HTTP是 ...

  2. GsonFormat根据返回值json快速构建Model

    Json是一个插件,我们只需要在Android studio中进行安装一下,即可使用. 根据平时的操作,根据浏览器中返回中的数据一行一行敲,其实这样非常麻烦. 有一个简单的方法,可以瞬间生成一个实体类 ...

  3. c++特性:指向类成员的指针和非类型类模板参数和函数指针返回值 参数推导机制和关联型别

    一.c++允许定义指向类成员的指针,包括类函数成员指针和类数据成员指针 格式如下: class A { public: void func(){printf("This is a funct ...

  4. Java反射机制二 获取方法的返回值或参数的泛型信息

    在使用反射机制时,我们经常需要知道方法的参数和返回值类型,很简单  ,下面上示例,示例中的两个方法非常相似 package deadLockThread; import java.lang.refle ...

  5. idea 快速生成返回值快捷方式

    idea java快速生成返回值   ctrl+alt+V

  6. 关于java中ArrayList的快速失败机制的漏洞——使用迭代器循环时删除倒数第二个元素不会报错

    一.问题描述 话不多说,先上代码: public static void main(String[] args) throws InterruptedException { List<Strin ...

  7. GetLastError()函数返回值及含义

    GetLastError返回的值通过在api函数中调用SetLastError或SetLastErrorEx设置.函数并无必要设置上一次错误信息,所以即使一次GetLastError调用返回的是零值, ...

  8. 快速失败机制--fail-fast

    fail-fast 机制是Java集合(Collection)中的一种错误机制.当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast(快速失败)事件.例如:当某一个线程A通过iter ...

  9. Python第七天 函数 函数参数 函数里的变量 函数返回值 多类型传值 函数递归调用 匿名函数 内置函数

    Python第七天   函数  函数参数   函数里的变量   函数返回值  多类型传值     函数递归调用   匿名函数   内置函数 目录 Pycharm使用技巧(转载) Python第一天   ...

随机推荐

  1. Android之探究viewGroup自定义子属性参数的获取流程

    通常会疑惑,当使用不同的布局方式时,子view得布局属性就不太一样,比如当父布局是LinearLayout时,子view就能有效的使用它的一些布局属性如layout_weight.weightSum. ...

  2. ZOJ Problem Set - 1240 IBM Minus One

    水题不解释,就是注意下格式,没输出一行字符串记得加一个空白行 #include <stdio.h> #include <string.h> int main() { ; ]; ...

  3. 高版本->低版本迁移,低版本客户端连接高版本数据库EXP导出报错EXP-00008,ORA-01455,EXP-00000

    生产环境: 源数据库:RHEL + Oracle 11.2.0.3 目标数据库:HP-UX + Oracle 10.2.0.4   需求:迁移部分表  11.2.0.3-->10.2.0.4,若 ...

  4. ssh整合问题总结--使用HibernateTemplate实现数据分页展示

    在进行大量的数据展示时,必须要使用分页查询,第一次使用在SSH框架整合中使用分页查询,遇到了一些问题,下面以我练习的项目为例详细介绍,如何在Spring+hibernate(+action)的环境下完 ...

  5. Apple的LZF算法解析

    有关LZF算法的相关解析文档比较少,但是Apple对LZF的开源,可以让我们对该算法进行一个简单的解析.LZFSE 基于 Lempel-Ziv ,并使用了有限状态熵编码.LZF采用类似lz77和lzs ...

  6. Get和Post区别

    1. get是从服务器上获取数据,post是向服务器传送数据.2. get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到.post是通过H ...

  7. httpCookie与Cookie安全

    Web 应用程序使用的 Cookie 个人认为这里设置的cookie与访问cookie的安全性关联大一点,配置节如下 <httpCookies domain="String" ...

  8. ul li设置横排,并除去li前的圆点

    效果预览:http://hovertree.com/texiao/css/ 如何用CSS制作横向菜单 让ul li横向排列及圆点处理 我们先建立一个无序列表,来建立菜单的结构.代码是: <ul& ...

  9. WPF模板

    WPF的中模板有三种:ControlTemplate.ItemsPanelTemplate.DataTemplate,他们继承抽象类FrameworkTemplate,下面是它们的继承关系: Wind ...

  10. EC笔记,第二部分:5.了解C++默默编写并调用哪些函数

    5.了解C++默默编写并调用哪些函数 1.C++空类 C++会为一个空类建立以下函数 (1).默认构造函数 (2).默认拷贝构造函数 (3).析构函数 (4).赋值运算符(如果成员包含引用类型或con ...