本文主要理解OC对象反汇编,以及block常见类型的反汇编
![f318852957c43b306afc3bbdf97329a4.png]()
OC反汇编
创建一个Person类,并在main函数中初始化一个Person对象
@interface Person : NSObject@property(nonatomic, copy) NSString *name;@property(nonatomic, assign) int age;
+ (instancetype)person;@end@implementation Person+ (instancetype)person{ return [[self alloc] init];
}@end<!--main.m中-->int main(int argc, char * argv[]) {
Person *p = [Person person]; return 0;
}
运行,查看其汇编代码
![d39d6f69f030534db425711c948ef54b.webp]()
首先作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发公众号:编程大鑫,不管你是小白还是大牛都欢迎入驻 ,让我们一起进步,共同发展!
1、静态调试
通过adrp+add获取地址,分别读取x0,x1
2、动态调试
通过一步一步执行汇编,来验证x0、x1是否如静态调试的结果一致?
![f4f6d956722399cc785e48aa66dfcc42.webp]()
通过调试发现,是一致的,其实这里的x0、x1就是 objc_msgSend的隐藏参数(id self,SEL _cmd)
下面我们继续调试汇编
点击step into,直接进入[Person person]方法(注意:这里不同iOS版本,多看到的汇编代码是有所区别的)
从这里看到ios13.4系统的alloc、init并不会走objc_msgSend
![ec23db9f5e4fccae39e20ba20915fc13.webp]()
ios11版本中,可以看到objc_msgSend,其本质是在调用init方法
![5e10b2b01758121999a823122f65bec3.webp]()
动态调试进行验证,结果如下所示,是一致的
![60d0334656d1f79e0df2ab53193f87a6.webp]()
查看此时的x0,已经是一个实例对象,因为alloc开辟了内存,已经分配了空间,具体的内部实现可以查看iOS-底层原理
![e74c1a1a247b6e3249ff5c08d95323d0.webp]()
疑问:为什么版本不同,调用不一样呢?
在不同的版本下,系统在运行时是不一样的。因为系统对alloc 、init进行了优化
接着往下看,点击step out 跳出[Person person],此时返回值在x0中
![66c4edb7bd6c845275f259df434e0742.webp]()
执行到bl ... objc_storeStrong,objc_storeStrong是OC中用strong修饰的对象底层都是调用这个函数,详情可以看iOS-底层原理
疑问:我们此时并没有使用strong修饰?:此时的局部变量p在此时就相当于一个强引用,是默认的。且这个方法执行完成后,相当于销毁p
![65e9210276e5e1e6557d3fe4e43a0e6e.webp]()
查看此时的x0、x1,相当于objc_storeStrong(&p,nil),将nil进行retain,将nil等于p(即 p=nil),p进行释放
![612207f3f6322bc5705818c5d7698a82.webp]()
查看objc_storeStrong源码
目的:对一个strong修饰的对象进行retain +1,对一个老的对象进行release
- 为什么是指针? 因为函数是值传递,而函数内部需要修改p的值
/*
- id *location 指向对象的指针 本质上是 &p(即局部变量地址)
- id obj 对象
目的:对一个strong修饰的对象进行retain +1,对一个老的对象进行release
为什么是指针? 因为函数是值传递,而函数内部需要修改p的值
*/voidobjc_storeStrong(id *location, id obj)
{ //prev 相当于p ,因为location是 &p
id prev = *location; //第二个参数 == 第一个参数,直接return
if (obj == prev) { return;
} //retain+1
objc_retain(obj); //修改p的值,指向第二个对象
*location = obj; //释放老对象
objc_release(prev);
}
相当于
Person *p = p1;
p = p2;//此时p1释放,p2retain+1
所以以上汇编中的objc_storeStrong(&p,nil)的实现代码如下
objc_storeStrong(&p,nil){ id prev = p; if nil == p{ return;
}
objc_retain(nil);
p = nil;//指针指向nil
objc_release(p);//释放堆空间}
[[self alloc] init] 优化过程
在最初的版本(iOS9)中,相当于两次消息发送 objc_msgSend
iOS11版本 是一次消息发送 objc_alloc + objc_msgSend
iOS13.5.1以上版本,已经没有objc_msgSend,而是objc_alloc_init
![23bf385ca464a875ee770fb467ab69de.webp]()
以上是LLDB动态调试Person *p = [Person person]; //objc_msgSend x0,x1
通过工具看复杂的OC代码
在上述OC代码的基础上增加一些代码,然后再来静态分析
int main(int argc, char * argv[]) {
Person *p = [Person person]; //objc_msgSend x0,x1
p.name = @"CJL";
p.age = 18; return 0;
}
CMD + B 编译程序,生成mach-o文件,并找到该文件
通过Hopper反汇编mach-o文件,main函数的分析如下
![bce44ddc060d95191ddda90124c73b24.webp]()
双击objc_cls_ref_Person,查看p的地址,是000000010000ce88,是在Data段
![87c85b7b3821642a5cb86195cfd6b5c4.webp]()
通过MachOView打开mach-o分析,查找000000010000ce88,与Hopper中的显示是一致的
![7f162eaf5c67f78dff154f41c495ceed.webp]()
双击@selector(person),查看person方法的反汇编
![1f6c9710ac12f961e04704d660d773ae.webp]()
双击0x10000cc68
![1474af0bb21d526996d30468ed9659e3.webp]()
双击“person”,地址为 0x10000752a
![1a287603b8624b4b2d50106e68b2b9dc.webp]()
在mach中查找0x10000752a,所有方法的name都在CString中
![8c6e37902af7840aafe71290c19be49c.webp]()
Block反汇编
定义一个block
int main(int argc, char * argv[]) { void(^block)(void) = ^(){
NSLog(@"block");
};
block(); return 0;
}
反汇编分析block的目的是想快速定位block的invoke,因为invoke中是实现代码,以下是block的汇编代码
![eef95eca74a5fb679e18f4ea7dcdc74c.webp]()
struct Block_layout{
void *isa; volatile int32_t flags; //contains ref count
int32_t reserved;
BlockInvokeFunction invoke; struct Block_descriptor_1 *descriptor;
//imported variables};
然后动态调试查看block的内存结构
![075de9ee5e951e5a7a63b6b63ece2db4.webp]()
如果block引用了外部变量呢?
定义一个block,其中block引用了外部变量,查看此时的汇编代码
int main(int argc, char * argv[]) { int a = 10; void(^block)(void) = ^(){
NSLog(@"block -- %d", a);
};
block(); return 0;
}
1、lldb调试
2、静态分析
通过hopper静态分析如下,以下是main函数的反汇编
![4fd2771f4fb41963303ce402cd7adcba.webp]()
双击___main_block_invoke,跳转至invoke的具体实现(并没有在main函数中,是单独的实现)
![9a743faab0256cff58e6cf8b955580ae.webp]()
双击___block_descriptor_36_e5_v8�?0l,是一个单独的描述
![6fd1c652b5afa26f97a5b9306173269b.webp]()
总结
首先作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发公众号:编程大鑫,不管你是小白还是大牛都欢迎入驻 ,让我们一起进步,共同发展!
[[self alloc] init] 优化过程
在最初的版本(iOS9)中,相当于两次消息发送 objc_msgSend
iOS11版本 是一次消息发送 objc_alloc + objc_msgSend
iOS13.5.1以上版本,已经没有objc_msgSend,而是objc_alloc_init
反汇编分析方式: