ARC在编译和运行做了什么?

ARC?

ARC即自动引用计数。具体介绍以及ARC规则可以看我之前写的另外一篇文章——ARC规则

本篇主要探究ARC在背后究竟做了什么?
先下结论,后续都是围绕这个展开的。
1.在编译期,ARC会把互相抵消的retain、release、autorelease操作约简。
2.ARC包含有运行期组件,可以在运行期检测到autorelease和retain这一对多余的操作。为了优化代码,在方法中返回自动释放的对象时,要执行一个特殊函数。

ARC简化引用计数

在Clang编译器项目带有一个静态分析器(static analyzer)用于指名程序里引用计数出问题的地方。
在使用ARC时一定要记住, **引用计数实际上还是要执行的, 只不过保留与释放操作现在是由ARC自动为你添加。**由于ARC会自动执行 retain、release、autorelease、decalloc等操作, 所以直接在ARC下调用这些内存管理方法是非法的。
实际上, ARC在调用这些方法时, 并不通过普通的 Objective-C消息派发机制,而是直接调用其底层C语言版本。这样做性能更好, 因为保留及释放操作需要频繁执行, 所以直接调用底层函数能节省很多CPU周期。

通过底层汇编看看ARC是怎么工作的

首先自定义一个类Test
该类有两个类方法,区别在于一个以new开头,调用者持有返回的对象,而create开头的则不持有。
这个也是对应了ARC的方法命名规则:
将内存管理语义在方法名中表示出来早已成为 Objective-C的惯例, 而ARC则将之确立为硬性规定。若方法名以下列词语开头, 则其返回的对象归调用者所有: alloc、new、copy、mutable Copy。若调用上述开头的方法就要负责释放返回的对象。也就是说, 这些对象在MRC中需要你手动的进行释放。若方法名不以上述四个词语开头, 返回的对象就不需要你手动去释放, 因为在方法内部将会自动执行一次autorelease方法。

//
//  Test.h
//  TestArc
//
//  Created by 差不多先生 on 2022/7/18.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Test : NSObject

+ (instancetype)createTest;
+ (instancetype)newTest;
@end

NS_ASSUME_NONNULL_END
#import "Test.h"

@implementation Test
+ (instancetype)createTest {
   
    return [[self alloc] init];
}
+ (instancetype)newTest {
   
    return [[self alloc] init];
}
@end

测试的情景:

#import "Test.h"
@interface ViewController ()
@property (nonatomic, strong) Test* test;
@end

@implementation ViewController

- (void)viewDidLoad {
   
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    // 自己持有返回对象
    [Test newTest]; // test1
//    id temp = [Test newTest]; // test2
//    self.test = [Test newTest]; // 3
//    // 非自己持有返回对象
    [Test createTest]; // 4
//    id temp2 = [Test createTest]; // 5
//    self.test = [Test createTest]; // 6
}

Test1

现在开始查看底层的汇编语言,可以通过编译器的debug调试。
在这里插入图片描述
可以看见主要出现了20 21 行的两个方法。
objc_msgSend: 这个就是向Test发送消息newTest。
objc_release: release的底层版本,释放newTest返回的对象。

可以看出ARC为我们自动添加了release操作,ARC自动添加代码应该如下:

id temp = [Test newTest];
objc_release(temp) ;

Test2

在这里插入图片描述
可以看到这次出现了一个新的底层函数objc_storeStrong:,相当于ARC在底层添加了
objc_storeStrong(&temp1, nil);看看objc库中其具体实现。

// strong
void
objc_storeStrong(id *location, id obj)
{
   
    id prev = *location;
    if (obj == prev) {
   
        return;
    }
    objc_retain(obj);
    *location = obj;
    objc_release(prev);
}

上面的代码做了以下四件事情:

  • 检查输入的 obj 地址 和指针指向的地址是否相同。
  • 持有对象,引用计数 + 1 。
  • 指针指向 obj。
  • 原来指向的对象引用计数 - 1。
    在本例子中相当于temp1持有newTest返回的对象,并且引用计数 + 1

Test3

在这里插入图片描述
这里发了两次消息,新增的消息是发送setTest的,ARC在setTest的加入了objc_storeStrong。
所以ARC填入后完整代码是:

id temp = [Test newTest];
    [self setTest:temp];
    objc_release(temp);
 - (void)setTest:(Test *test) {
   
    objc_storeStrong(&_test, test);
}

Test4

接下来的4 5 6都采用了createTest,所以不持有返回对象,不需要手动释放。
在这里插入图片描述
这里ARC修改后的代码应该是这样的:

// Test
+ (instancetype)createTest {
   
    id temp = [self new];  
    return objc_autoreleaseReturnValue(temp); 
} 
// VC
- (void)testForARC {
    
    objc_unsafeClaimAutoreleasedReturnValue([Test createTest]); 
}


这里可以看出不仅仅多了一个objc_unsafeClaimAutoreleasedReturnValue,并且在create中多了objc_autoreleaseReturnValue,这是因为ARC规则,无需手动释放的内部自动autorelease。
objc_autoreleaseReturnValue: 这个函数的作用相当于代替我们手动调用 autorelease, 创建了一个autorelease对象。编译器会检测之后的代码, 根据返回的对象是否执行 retain操作, 来设置全局数据结构中的一个标志位, 来决定是否会执行 autorelease操作。该标记有两个状态, ReturnAtPlus0代表执行 autorelease, 以及ReturnAtPlus1代表不执行 autorelease

objc_unsafeClaimAutoreleasedReturnValue: 这个函数的作用是对autorelease对象不做处理仅仅返回,对非autorelease对象调用objc_release函数并返回。所以本情景中它创建时执行了 autorelease操作了,就不会对其进行 release操作了。只是返回了对象,在合适的实际autoreleasepool会对其进行释放的。

Test5

在这里插入图片描述
objc_retainAutoreleasedReturnV

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值