Created
February 19, 2019 22:17
-
-
Save xhruso00/69313d5492142ebe86a4da69091fa102 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
点我达iOS首页 | |
iOS Block探究 | |
2017/12/28 | |
### | |
iOS Block探究 | |
Block 结构体 | |
对应的结构体定义如下: | |
struct Block_descriptor { | |
unsigned long int reserved; // NULL | |
unsigned long int size; // sizeof(struct Block_literal_1) | |
void (*copy_helper)(void *dst, void *src); // IFF (1<<25) | |
void (*dispose_helper)(void *src); // IFF (1<<25) | |
const char *signature; // IFF (1<<30) | |
} *descriptor; | |
struct Block_layout { | |
void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock | |
int flags; | |
int reserved; | |
void (*invoke)(void *, ...); | |
struct Block_descriptor *descriptor; | |
/* Imported variables. */ | |
}; | |
isa 指针(Block也是一个对象); | |
flags 用bit位表示一些Block的附加信息(判断Block类型、判断Block引用计数、判断Block是否需要执行辅助函数); | |
reserved 保留变量; | |
invoke 函数指针,指向具体的 Block 实现的函数调用地址; | |
descriptor Block 的附加描述信息; Block详述参考 | |
我们需要注意的是: | |
`void (*invoke)(void *, ...)` | |
指向 Block 具体实现地址 | |
`const char *signature` | |
表示 Block 函数签名的字符串 | |
`在64位系统上,指针类型的大小是8个字节,而int是4个字节` | |
Block 的内存管理 | |
在 Objective-C 语言中,一共有 3 种类型的 Block: | |
_NSConcreteGlobalBlock 全局的静态不会访问外部局部变量(无外部变量或者全局变量) | |
_NSConcreteStackBlock 保存在栈中,当函数返回时会被销毁 | |
_NSConcreteMallocBlock 保存在堆中,当引用计数为 0 时会被销毁 | |
如下示例: | |
#import "ViewController.h" | |
@interface ViewController () | |
@property (copy, nonatomic) void (^tryBlock)(void); | |
@end | |
@implementation ViewController | |
- (void)viewDidLoad { | |
[super viewDidLoad]; | |
//__block ViewController *weakSelf = self; | |
self.tryBlock = ^{ | |
// NSLog(@"%@", weakSelf.tryBlock); // _NSConcreteMallocBlock | |
}; | |
NSLog(@"%@", self.tryBlock); // _NSConcreteGlobalBlock | |
} | |
MRC 模式下 Block 的内存地址在栈区,防止被销毁而使用修饰词copy,将 Block 存放到堆区(Copy修饰词将 Block 从栈区复制到堆区) | |
ARC 模式下系统会默认对 Block 进行copy操作 | |
Block 逆向处理 | |
使用hopper对 App 的二进制文件进行反汇编获得 Block 的实现代码 | |
0000000100026190 ldr x19, [x8, #0x878] "setConfirmeBlock:",@selector(setConfirmeBlock:) | |
0000000100026194 mov x0, x20 | |
0000000100026198 bl imp___stubs__objc_retain | |
000000010002619c mov x20, x0 | |
00000001000261a0 mov x2, sp | |
00000001000261a4 mov x0, x21 | |
00000001000261a8 mov x1, x19 | |
00000001000261ac bl imp___stubs__objc_msgSend | |
添加断点触发: | |
(lldb) po $x0 | |
<RiderAlertView: 0x15f843080; frame = (0 0; 375 667); layer = <CALayer: 0x17003d4e0>> | |
(lldb) po (char *)$x1 | |
"setConfirmeBlock:" | |
(lldb) po $x2 | |
<__NSStackBlock__: 0x16fde16f0> | |
根据指针<__NSStackBlock__: 0x16fde16f0>打印内存地址: | |
(lldb) memory read --size 8 --format x 0x16fde16f0 | |
0x16fde16f0: 0x000000019bf4d088 0x00000000c2000000 | |
0x16fde1700: 0x00000001000421f4 0x000000010072aab0 | |
0x16fde1710: 0x000000015f822170 0x000000015c6e5c70 | |
0x16fde1720: 0x000000015f822170 0x00000001005f15b5 | |
函数指针(invoke)的地址是在第 16 个字节之后,并占用 8 个字节 | |
void *isa; // 8 | |
int flags; // 4 | |
int reserved; // 4 | |
void (*invoke)(void *, ...); | |
得到函数的地址是0x00000001000421f4,使用下面的命令对地址反汇编可以获得与hopper中完全一样的代码! | |
(lldb) disassemble --start-address 0x00000001000421f4 | |
要查看 Block 的参数需要找到const char *signature的地址,signature的地址是在descriptor下偏移两个unsiged long和两个指针后的地址(32 个字节后) | |
unsigned long int reserved; // NULL | |
unsigned long int size; // sizeof(struct Block_literal_1) | |
void (*copy_helper)(void *dst, void *src); // IFF (1<<25) | |
void (*dispose_helper)(void *src); // IFF (1<<25) | |
const char *signature; // IFF (1<<30) | |
根据descriptor(0x000000010072aab0)获取signature(0x00000001006b5196) | |
(lldb) memory read --size 8 --format x 0x000000010072aab0 | |
0x10072aab0: 0x0000000000000000 0x0000000000000030 (unsiged long) | |
0x10072aac0: 0x00000001000422a8 0x00000001000422d0 (两个指针) | |
0x10072aad0: 0x00000001006b5196 0x0000000000000200 | |
0x10072aae0: 0x0000000000000000 0x0000000000000020 | |
打印字符串类型: | |
(lldb) p (char *)0x00000001006b5196 | |
(char *) $3 = 0x00000001006b5196 "v8@?0" | |
(lldb) po [NSMethodSignature signatureWithObjCTypes:"v8@?0"] | |
<NSMethodSignature: 0x17427be80> | |
number of arguments = 1 | |
frame size = 224 | |
is special struct return? NO | |
return value: -------- -------- -------- -------- | |
type encoding (v) 'v' | |
flags {} | |
modifiers {} | |
frame {offset = 0, offset adjust = 0, size = 0, size adjust = 0} | |
memory {offset = 0, size = 0} | |
argument 0: -------- -------- -------- -------- | |
type encoding (@) '@?' | |
flags {isObject, isBlock} | |
modifiers {} | |
frame {offset = 0, offset adjust = 0, size = 8, size adjust = 0} | |
memory {offset = 0, size = 8} | |
针对获取的type encoding字段对比Type Encoding 官方文档我们所分析的Block没有返回值,也没有参数!! | |
获取 Block 代码源示例 | |
常规 Block | |
00000001000261f4 dd 0xa9be4ff4 ; DATA XREF=-[MoreViewController loginOutButtonClicked:]+120 | |
00000001000261f8 stp x29, x30, [sp, #0x10] | |
00000001000261fc add x29, sp, #0x10 | |
0000000100026200 mov x19, x0 | |
0000000100026204 ldr x0, [x19, #0x20] | |
0000000100026208 adrp x8, #0x1008ae000 | |
000000010002620c ldr x1, [x8, #0x9c8] | |
0000000100026210 bl imp___stubs__objc_msgSend | |
0000000100026214 mov x29, x29 | |
0000000100026218 bl imp___stubs__objc_retainAutoreleasedReturnValue | |
000000010002621c mov x20, x0 | |
0000000100026220 adrp x8, #0x1008ae000 | |
0000000100026224 ldr x1, [x8, #0xa48] | |
0000000100026228 bl imp___stubs__objc_msgSend | |
000000010002622c mov x0, x20 | |
0000000100026230 bl imp___stubs__objc_release | |
0000000100026234 ldr x0, [x19, #0x20] | |
0000000100026238 adrp x8, #0x1008ad000 | |
000000010002623c ldr x1, [x8, #0xa68] | |
0000000100026240 bl imp___stubs__objc_msgSend | |
0000000100026244 mov x29, x29 | |
0000000100026248 bl imp___stubs__objc_retainAutoreleasedReturnValue | |
000000010002624c mov x20, x0 | |
0000000100026250 ldr x2, [x19, #0x28] | |
0000000100026254 adrp x8, #0x1008ae000 | |
0000000100026258 ldr x1, [x8, #0xa50] | |
000000010002625c bl imp___stubs__objc_msgSend | |
0000000100026260 mov x0, x20 | |
0000000100026264 bl imp___stubs__objc_release | |
0000000100026268 adrp x8, #0x1008c7000 | |
000000010002626c ldr x0, [x8, #0x1a8] | |
0000000100026270 adrp x8, #0x1008ad000 | |
0000000100026274 ldr x1, [x8, #0x2a0] | |
0000000100026278 bl imp___stubs__objc_msgSend | |
000000010002627c mov x29, x29 | |
0000000100026280 bl imp___stubs__objc_retainAutoreleasedReturnValue | |
0000000100026284 mov x19, x0 | |
0000000100026288 adrp x8, #0x1008ad000 | |
000000010002628c ldr x1, [x8, #0x558] | |
0000000100026290 movz w2, #0x4f | |
0000000100026294 bl imp___stubs__objc_msgSend | |
0000000100026298 mov x0, x19 | |
000000010002629c ldp x29, x30, [sp, #0x10] | |
00000001000262a0 ldp x20, x19, [sp]!, #0x20 | |
00000001000262a4 b imp___stubs__objc_release | |
这段 Block 代码在hopper中是不会生成伪代码的,这就需要我们手动操作了 | |
连接lldb | |
在bl imp___stubs__objc_msgSend处添加断点 | |
使用po 命令 | |
就上述代码添加断点: | |
(lldb) br s -a 0x00000000000cc000+0x0000000100026210 | |
Breakpoint 1: where = Rider`_mh_execute_header + 132092, address = 0x00000001000f2210 | |
(lldb) br s -a 0x00000000000cc000+0x0000000100026228 | |
Breakpoint 2: where = Rider`_mh_execute_header + 132116, address = 0x00000001000f2228 | |
(lldb) br s -a 0x00000000000cc000+0x0000000100026240 | |
Breakpoint 3: where = Rider`_mh_execute_header + 132140, address = 0x00000001000f2240 | |
(lldb) br s -a 0x00000000000cc000+0x000000010002625c | |
Breakpoint 4: where = Rider`_mh_execute_header + 132168, address = 0x00000001000f225c | |
(lldb) br s -a 0x00000000000cc000+0x0000000100026278 | |
Breakpoint 5: where = Rider`_mh_execute_header + 132196, address = 0x00000001000f2278 | |
(lldb) br s -a 0x00000000000cc000+0x0000000100026294 | |
Breakpoint 6: where = Rider`_mh_execute_header + 132224, address = 0x00000001000f2294 | |
触发第一个断点,执行命令 | |
(lldb) c | |
Process 10503 resuming | |
Process 10503 stopped | |
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 | |
frame #0: 0x00000001000f2210 Rider`_mh_execute_header + 156176 | |
Rider`_mh_execute_header: | |
-> 0x1000f2210 <+156176>: bl 0x10069660c ; symbol stub for: objc_msgSend | |
0x1000f2214 <+156180>: mov x29, x29 | |
0x1000f2218 <+156184>: bl 0x10069666c ; symbol stub for: objc_retainAutoreleasedReturnValue | |
0x1000f221c <+156188>: mov x20, x0 | |
(lldb) po $x0 | |
<MoreViewController: 0x14d6dbd10> | |
(lldb) po (char *)$x1 | |
"getMainViewController" | |
(lldb) po $x2 | |
208 | |
(lldb) po $x3 | |
<nil> | |
(lldb) po $x4 | |
<nil> | |
触发第二个断点 | |
(lldb) c | |
Process 10503 resuming | |
Process 10503 stopped | |
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1 | |
frame #0: 0x00000001000f2228 Rider`_mh_execute_header + 156200 | |
Rider`_mh_execute_header: | |
-> 0x1000f2228 <+156200>: bl 0x10069660c ; symbol stub for: objc_msgSend | |
0x1000f222c <+156204>: mov x0, x20 | |
0x1000f2230 <+156208>: bl 0x10069663c ; symbol stub for: objc_release | |
0x1000f2234 <+156212>: ldr x0, [x19, #0x20] | |
(lldb) po $x0 | |
<MainViewController: 0x14d5b2020> | |
(lldb) po (char *)$x1 | |
"setOrderListForceRefresh" | |
(lldb) po $x2 | |
还原成OC代码应该是: [MoreViewController getMainViewController] 与 [MainViewController setOrderListForceRefresh] | |
这些方法调用在MoreViewController某个 Block 方法中,所以完整的方法实现应该是这样[[self getMainViewController] setOrderListForceRefresh]。 | |
以此类推直到触发第四个断点时打印数据如下: | |
(lldb) po (char *)$x1 | |
"loginOutRequestWithRequestWidgetId:" | |
(lldb) po $x0 | |
<MoreViewModel: 0x17067bd80> | |
(lldb) po $x2 | |
<Button: 0x14d5da9f0; baseClass = UIButton; frame = (0 185; 375 45); clipsToBounds = YES; opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x1744366c0>> | |
(lldb) po $x3 | |
<nil> | |
触发所有的断点得到OC代码: | |
[[MoreViewController getMainViewController] setOrderListForceRefresh] | |
[MoreViewModel loginOutRequestWithRequestWidgetId: Button] | |
[UMengManager shared] sendUMengEventRequest:79] | |
对比源码: | |
alertview.confirmeBlock = ^{ | |
[[self getMainViewController] setOrderListForceRefresh]; | |
[self.viewModel loginOutRequestWithRequestWidgetId:sender]; | |
[[UMengManager shared] sendUMengEventRequest:UMengEventExitLogin]; | |
}; | |
参考: http://www.swiftyper.com/2016/12/16/debuging-objective-c-blocks-in-lldb/ http://linlexus.com/implementation_of_block/ | |
--> | |
© 2015 点我达iOS TOP | |
首页 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment