OC Autorelease

@implementation ViewController
- (void)viewDidLoad {[super viewDidLoad];__unsafe_unretained NSObject *obj1 = [ViewController getObj];NSLog(@"=====%@",obj1); // 运行OK__unsafe_unretained NSObject *obj2 = [ViewController getObj];NSLog(@"=====%@",obj2); // crash 野指针
}+ (id)getObj {return [NSObject new];
}
复制代码
- (void)viewDidLoad {[super viewDidLoad];__weak NSObject *obj1 = [ViewController getObj];NSLog(@"=====%@",obj1); // nil__weak NSObject *obj2 = [ViewController getObj];NSLog(@"=====%@",obj2);// 输出对象
}
复制代码

Autorelease与Autoreleasepool

参考: ARC环境下编译器到底对autorelease对象做了怎样的优化 黑幕背后的Autorelease 自动释放池的前世今生 ---- 深入解析 Autoreleasepool Objective-C 小记(8)autorelease

####autoreleasepool 1、自动释放池是由 AutoreleasePoolPage 以双向链表的方式实现的 2、当对象调用 autorelease 方法时,会将对象加入 AutoreleasePoolPage 的栈中 3、调用 AutoreleasePoolPage::pop 方法会向栈中的对象发送 release 消息 @autoreleasepool{}

@autoreleasepool {__autoreleasing NSObject *obj = [NSObject new];}
复制代码

伪代码

    // 获取哨兵POOL_SENTINELvoid * atautoreleasepoolobj = objc_autoreleasePoolPush();{__autoreleasing NSObject *obj = [NSObject new];}// 就是release哨兵之后的autorelease对象。objc_autoreleasePoolPop(atautoreleasepoolobj);
复制代码

autorelease调用栈

- [NSObject autorelease]
└── id objc_object::rootAutorelease()└─ id objc_object::rootAutorelease2()└─ static id AutoreleasePoolPage::autorelease(id obj)└─ static id AutoreleasePoolPage::autoreleaseFast(id obj)├─ id *add(id obj)├─ static id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)│  ├─ AutoreleasePoolPage(AutoreleasePoolPage *newParent)│  └─ id *add(id obj)└─ static id *autoreleaseNoPage(id obj)├─ AutoreleasePoolPage(AutoreleasePoolPage *newParent)└─ id *add(id obj)
复制代码

#####一个autorelease对象在什么时刻释放? 1、手动指定Autoreleasepool:当前Autoreleasepool作用域大括号结束时释放;

2、不手动指定:autorelease对象会被添加到最近一次创建的autoreleasepool中,并在当前的runloop迭代结束时候释放。

例如:主Runloop对Autoreleasepool管理的流程: Runloop中,检测到触摸事件,创建事件,创建Autoreleasepool,autorelease对象加入pool中,事件完成,Runloop运行循环将要结束,释放Autoreleasepool,向pool中对象发送release消息,Runloop休眠。 ####autorelease 进行的非持有方法的优化 1、alloc/new/copy/mutableCopy---持有对象方法 2、其他类方法返回的对象,如果下面的createObj

@implementation BBObject
+ (instancetype)createObj {return [self new];
}
复制代码

需要了解下面的方法:

id objc_autoreleaseReturnValue(id obj)
{// prepareOptimizedReturn判断是否可以TSL优化,可以则标记,YES--就不需要调用 objc_autorelease(),优化性能if (prepareOptimizedReturn(ReturnAtPlus1)) return obj;return objc_autorelease(obj);
}
id objc_retainAutoreleasedReturnValue(id obj)
{// 如果之前 objc_autoreleaseReturnValue() 存入的标志位为 ReturnAtPlus1,则直接返回对象,无需调用 objc_retain(),优化性能if (acceptOptimizedReturn() == ReturnAtPlus1) return obj;return objc_retain(obj);
}
复制代码
static ALWAYS_INLINE bool 
prepareOptimizedReturn(ReturnDisposition disposition)
{assert(getReturnDisposition() == ReturnAtPlus0);if (callerAcceptsOptimizedReturn(__builtin_return_address(0))) {if (disposition) setReturnDisposition(disposition);return true;}return false;
}static ALWAYS_INLINE ReturnDisposition 
acceptOptimizedReturn()
{ReturnDisposition disposition = getReturnDisposition();setReturnDisposition(ReturnAtPlus0);  // reset to the unoptimized statereturn disposition;
}
复制代码

TLS 全称为 Thread Local Storage,是每个线程专有的键值存储

在某个线程上的函数调用栈上相邻两个函数对 TLS 进行了存取,这中间肯定不会有别的程序『插手』。
所以 getReturnDisposition() 和 setReturnDisposition() 的实现比较简单,不需要判断考虑是针对哪个对象的 Disposition 进行存取,因为当前线程上下文中只处理唯一的对象,保证不会乱掉。 static ALWAYS_INLINE void 
setReturnDisposition(ReturnDisposition disposition)
{tls_set_direct(RETURN_DISPOSITION_KEY, (void*)(uintptr_t)disposition);
}
复制代码

callerAcceptsOptimizedReturn(__builtin_return_address(0))函数在不同架构的 CPU 上实现也是不一样的。 主要作用:

__builtin_return_address(0)获取当前函数返回地址,传入 callerAcceptsOptimizedReturn 判断调用方是否紧接着调用了 objc_retainAutoreleasedReturnValue 当判断调用方紧接着调用了 objc_retainAutoreleasedReturnValue 或者 objc_unsafeClaimAutoreleasedReturnValue 直接当前对象地址,而不执行retain与autorelease操作.

#####ARC 会视情况在调用方法时可能会添加 retain ,在方法内部返回时可能会添加 autorelease ,经过优化后很可能会抵消。

#####1、持有、无引用

- (void)test {[BBObject new];
}
复制代码

编译器编译后的伪代码

- (void)test {objc_release([BBObject new]) ;
}
复制代码

#####2、持有、局部变量引用 __strong

- (void)test {__strong BBObject * obj = [BBObject new];
}
复制代码

编译器编译后的伪代码

- (void)test {id temp = [BBObject new];objc_storeStrong(&tmp,nil);//相当于tmp指向对象执行release
}
复制代码

__weak、__unsafe_unretained

    // 这种写法xcode提示警告__weak BBObject * obj = [BBObject new]; __unsafe_unretained BBObject * obj = [BBObject new];
复制代码

#####3、持有、外部变量引用

- (void)test {self.obj = [BBObject new];
}
复制代码

编译器编译后的伪代码

- (void)test{id temp = [BBObject new];[self setObj:temp];//setter方法执行objc_storeStrongobjc_release(temp);
}
- (void)setObj:(id aObj) {objc_storeStrong(&_obj, aObj);
}
复制代码

#####4、不持有、无引用

- (void)test {[BBObject createObj];
}
复制代码

编译器编译后的伪代码

+ (instancetype) createObj {id tmp = [self new];return objc_autoreleaseReturnValue(tmp); // 系统可能会调用[tmp autorelease] 
}
- (void)test { objc_unsafeClaimAutoreleasedReturnValue([BBObject createObj]); 
}
复制代码

#####5、不持有、局部变量引用


- (void)test {BBObject * obj1 = [BBObject createObj];
}
复制代码

编译器编译后的伪代码

+ (instancetype) createObj {id tmp = [self new];return objc_autoreleaseReturnValue(tmp); // 系统可能会调用[tmp autorelease] 
}
- (void)test {id obj1 = objc_retainAutoreleasedReturnValue([BBObject createObj]);  objc_storeStrong(& obj1,nil); 
}
复制代码

发现obj1指向的对象不会加入autoreleasepool

#####6、不持有、外部变量引用

+ (instancetype) createObj {id tmp = [self new];return objc_autoreleaseReturnValue(tmp); // 系统可能会调用[tmp autorelease] 
}
- (void)test {self.obj = [BBObject createObj];
}
复制代码

编译后的伪代码

- (void)test {id tmp = _objc_retainAutoreleasedReturnValue([Foo createFoo]); [self setObj:temp]; // setter方法执行objc_storeStrongobjc_release(temp);
}
复制代码

查看autoreleasepool中的对象方法: 1、extern void _objc_autoreleasePoolPrint(void); //extern这个方法,需要查看的地方使用_objc_autoreleasePoolPrint();

2、需要查看的地方打断点,然后po _objc_autoreleasePoolPrint()

转载于:https://juejin.im/post/5a312182f265da4304069e3d

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/256023.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【opencv】——钢管计数(霍夫圆变换 + 阈值 + canny)

目录 方法一:霍夫圆变换 + canny 方法二 阈值 + 寻边 对图中的钢管进行计数 方法一:霍夫圆变换 + canny

svn服务器搭建-SuSE Linux Enterprise Server 11 SP3

svn存储版本数据也有2种方式:1.bdb;2.fsfs。因为BDB方式在服务器中断时,有可能锁住数据(搞ldap时就深受其害,没法根治),所以还是FSFS方式更安全一点,我也选择这种方式。下载相关软件…

Swift 2.0初探:值得注意的新特性

转眼间,Swift已经一岁多了,这门新鲜、语法时尚、类型安全、执行速度更快的语言已经渐渐的深入广大开发者的心。我同样也是非常喜爱这门新的编程语言。 今年6月,一年一度的WWDC大会如期而至,在大会上Apple发布了Swift 2.0&#xff…

Android 自定义WebView弹窗及屏蔽弹窗

额,还是那个WebView的问题,内核已换成腾讯X5内核,所以接下来的内容会有一些X5内核的方法。但我们的H5是不能改的,还是只有委屈我们自己。先看看H5自带的弹窗 这样子的弹窗在不同的手机上呈现的可能是不同的效果,效果不…

【图像处理】——Python实现two_pass方法来进行连通域的提取

目录 一、相关知识 1、two_pass算法思想 2、并查集算法 二、自定义的two_pass算法

C++ 多线程使用future传递异常

如果 std::async 调用的函数抛出异常,那么这个异常会被存储在值的位置,同时 future 变为 ready ,如果调用 get() 会重新抛出存储的异常。 Note: 标准并没有指定原来的异常对象是被重新抛出或者拷贝后抛出,不同的编译器会做不同的选择。 对于 …

期货黄金与现货黄金比较

现货黄金与期货黄金是目前市场上最热门的黄金投资方式,与国内任何的金融投资品相比,都具有一定的优势。 其实金投网小编觉得现货黄金与期货黄金最主要的不同点是这个:期货黄金做的是国内市场,同股票市场一样,里面有庄家…

DNS域传送漏洞

0x00 相关背景介绍 Dns是整个互联网公司业务的基础,目前越来越多的互联网公司开始自己搭建DNS服务器做解析服务,同时由于DNS服务是基础性服务非常重要,因此很多公司会对DNS服务器进行主备配置而DNS主备之间的数据同步就会用到dns域传送&#…

封装之--通过类中公有方法访问私有成员变量

如何在ClassB中访问ClassA的私有成员变量?(典型的封装案例) 通过在ClassA中定义公有的成员方法,然后,在ClassB中通过ClassA的对象调用ClassA中的公有方法,来访问ClassA中的私有成员变量。 转载于:https://w…

匹配物镜放大倍数与相机像元尺寸

通常来说,相机内部的CCD或者CMOS传感器上都有感光阵列,由一个一个的感光元件构成,每一个感光元件负责完成光电转换的过程。简单理解,一个感光元件可以认为就是一个像素(pixel)或像元(pel)。像元具有一定尺寸,如果像的尺…

2016/11/10 kettle概述

ETL(Extract-Transform-Load,即抽取,转换,加载),数据仓库技术,是用来处理将数据从来源(以前做的项目)经过抽取,转换,加载到达目的端(正在做的项目&#xff09…

【深度学习】——非极大值抑制(nms/soft-nms)

目录 一、相关概念 1、iou 1)理论计算 2)Python代码(代码参考yolov3模型util.py文件) 2、nms 1)基本思路 2)标准nms和soft-nms 3)Python代码实现(yolov3中util.py文件,增加了…

移动服务安全现状分析!

2019独角兽企业重金招聘Python工程师标准>>> 由于Android开源的环境,导致Android的整体环境都存在很多不安全的因素,同时用户在移动APP客户端的便捷应用,也给用户带来了巨大的安全隐患。未经过移动服务安全加固的APP存在被静态反编…

封装不同类模板的随机数生成器

最近准备刷题,打算简单封装下随机数生成器,方便产生测试数据。C11的STL提供了很多分布类型,我比较常用的是均匀分布,均匀分布的值有两种类型,一类是整数,另一类是浮点数,STL根据值的类型定义了两…

Log4j详细设置说明

1. 动态的改变记录级别和策略,即修改log4j.properties,不需要重启Web应用,这需要在web.xml中设置一下。2. 把log文件定在 /WEB-INF/logs/ 而不需要写绝对路径。3. 可以把log4j.properties和其他properties一起放在/WEB-INF/ ,而不是Class-Pat…

【机器学习】——卷积神经网络(Keras)修改学习率(定值+自适应)

目录 1、直接获取现有模型的学习率 2、打印显示学习率 3、调整学习率 1)1.LearningRateScheduler 2)ReduceLROnPlateau 利用tensorflow的Keras模块我们可以建立我们自己定义的卷积神经网络模型,但是一般不会触碰到学习率这个问题&#x…

使用Docker构建你的第一个服务

1. 感受一下Docker的便捷 项目源码 https://github.com/MoonShining/dockernize-grape-helloworld clone下来以后运行,cd到项目目录下,运行 docker build -t api-sample . docker run -p 8080:8080 api-sample:latest 复制代码就可以访问localhost:8080看到效果了. …

MyEclipse连接MySQL

在官网http://www.mysql.com/downloads/下载数据库连接驱动 本文中使用驱动版本为mysql-connector-java-5.1.40 一、创建一个java测试项目MySQLConnectorsTest 在项目下穿件一个lib文件夹用来存放MySQL驱动包。 右键驱动包build path进行add添加操作,打开Referenced…

在Windows系统中配置Google AddressSanitizer

Google AddressSanitizer简介 AddressSanitizer (ASan) 是 C 和 C 的内存错误检测软件,它可以检测: 释放指针后继续使用堆缓冲区溢出栈缓冲区溢出全局缓冲区溢出返回后继续使用在范围之外继续使用初始化顺序的bug内存泄漏 在 Windows 系统中&#xff…

【剑指offer】——求出一个正整数的质数因子(Python)

目录 一、题目描述 二、思路 1、短除法 2、平方根法 一、题目描述 功能:输入一个正整数,按照从小到大的顺序输出它的所有质因子(重复的也要列举)(如180的质因子为2 2 3 3 5 ) 最后一个数后面也要有空格 输入描述…