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_SENTINEL
    void * 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 state
    return 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_storeStrong
    objc_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_storeStrong
    objc_release(temp);
}
复制代码

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值