
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;
}
-
运行,查看其汇编代码

首先作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发公众号:编程大鑫,不管你是小白还是大牛都欢迎入驻 ,让我们一起进步,共同发展!
1、静态调试
通过adrp+add获取地址,分别读取x0,x1
-
读取x0,读出来是Person:
x 0x100c68eb0+po 0x0100c68f98
-
读取x1,读取出来是person方法:
x 0x100c68e88+p (SEL)0x01c019aef5
2、动态调试
通过一步一步执行汇编,来验证x0、x1是否如静态调试的结果一致?

通过调试发现,是一致的,其实这里的x0、x1就是 objc_msgSend的隐藏参数(id self,SEL _cmd)
下面我们继续调试汇编
-
点击step into,直接进入
[Person person]方法(注意:这里不同iOS版本,多看到的汇编代码是有所区别的)-
从这里看到ios13.4系统的alloc、init并不会走objc_msgSend

-
ios11版本中,可以看到objc_msgSend,其本质是在调用init方法

动态调试进行验证,结果如下所示,是一致的

查看此时的x0,已经是一个实例对象,因为alloc开辟了内存,已经分配了空间,具体的内部实现可以查看iOS-底层原理

疑问:为什么版本不同,调用不一样呢?
-
在不同的版本下,系统在运行时是不一样的。因为系统对alloc 、init进行了优化
-
-
接着往下看,点击step out 跳出[Person person],此时返回值在x0中

-
执行到
bl ... objc_storeStrong,objc_storeStrong是OC中用strong修饰的对象底层都是调用这个函数,详情可以看iOS-底层原理
疑问:我们此时并没有使用strong修饰?:此时的局部变量p在此时就相当于一个强引用,是默认的。且这个方法执行完成后,相当于销毁p
查看此时的
x0、x1,相当于objc_storeStrong(&p,nil),将nil进行retain,将nil等于p(即 p=nil),p进行释放
-
查看
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);//释放堆空间}
-
下面来进行动态验证,发现
Person对象指向nil
[[self alloc] init] 优化过程
-
在最初的版本(iOS9)中,相当于两次消息发送
objc_msgSend -
iOS11版本 是一次消息发送
objc_alloc + objc_msgSend -
iOS13.5.1以上版本,已经没有objc_msgSend,而是
objc_alloc_init
以上是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函数的分析如下

-
双击
objc_cls_ref_Person,查看p的地址,是000000010000ce88,是在Data段
通过
MachOView打开mach-o分析,查找000000010000ce88,与Hopper中的显示是一致的
-
双击
@selector(person),查看person方法的反汇编
双击
0x10000cc68
双击
“person”,地址为 0x10000752a
在mach中查找
0x10000752a,所有方法的name都在CString中
Block反汇编
定义一个block
int main(int argc, char * argv[]) { void(^block)(void) = ^(){
NSLog(@"block");
};
block(); return 0;
}
反汇编分析block的目的是想快速定位block的invoke,因为invoke中是实现代码,以下是block的汇编代码

-
查看
x0是什么?:是一个__block_literal_global,是一个全局静态block(即block不引用block外部变量,在编译时期就可以确定内存的分配等操作,存在于可执行文件的常量区),其他详情也可查看iOS-底层原理
以下是源码中block的定义,是一个结构体
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的内存结构

-
是否可以通过hopper查看 adrp + add 是一个block?
答案是可以的
-
双击
___block_literal_global
-
双击
0x0000000100006838,查看invoke
-
双击
0x0000000100008008,查看descriptor,和Block的源码结构类似
-
如果block引用了外部变量呢?
定义一个block,其中block引用了外部变量,查看此时的汇编代码
int main(int argc, char * argv[]) { int a = 10; void(^block)(void) = ^(){
NSLog(@"block -- %d", a);
};
block(); return 0;
}
1、lldb调试
-
以下是代码的汇编

-
验证是否是block的isa指针

adrp x10, 2获取指针地址ldr x10, [x10]:取值
-
查看此时block的内存,找到invoke(由于invoke是代码实现,所以需要由
dis -s(将代码的汇编打印出来)查看)
2、静态分析
-
通过hopper静态分析如下,以下是main函数的反汇编

-
双击
___main_block_invoke,跳转至invoke的具体实现(并没有在main函数中,是单独的实现)
-
双击
___block_descriptor_36_e5_v8�?0l,是一个单独的描述
总结
首先作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发公众号:编程大鑫,不管你是小白还是大牛都欢迎入驻 ,让我们一起进步,共同发展!
-
[[self alloc] init] 优化过程
-
在最初的版本(iOS9)中,相当于两次消息发送
objc_msgSend -
iOS11版本 是一次消息发送
objc_alloc + objc_msgSend -
iOS13.5.1以上版本,已经没有objc_msgSend,而是
objc_alloc_init
-
-
反汇编分析方式:
-
通过
LLDB动态调试 -
通过
Hopper + MachOView静态分析
-
文章版权声明
1 原创文章作者:shiba,如若转载,请注明出处: https://www.52hwl.com/35078.html
2 温馨提示:软件侵权请联系469472785#qq.com(三天内删除相关链接)资源失效请留言反馈
3 下载提示:如遇蓝奏云无法访问,请修改lanzous(把s修改成x)
4 免责声明:本站为个人博客,所有软件信息均来自网络 修改版软件,加群广告提示为修改者自留,非本站信息,注意鉴别
微信扫一扫
支付宝扫一扫