之前说了block的小探究,大家都知道block是个类。但是他又不像类那样调用方法。参数啥、回调函数都在结构体里面。
NSConcreteGlobalBlock。这种很简单。点进去都可以看到相关结构。
NSConcreteStackBlock,基本上回调函数存在X8寄存器.
最近我发现在IOS13打印Block有了更清楚的信息:
<__NSMallocBlock__: 0x2809553b0>
signature: "v16@?0@"NSString"8"
invoke : 0x10227dcf4 (/private/var/containers/Bundle/Application/893409BD-1E16-4DBC-8D29-7DDC1355E2A5/dylibLoad.app/dylibLoad`__57-[AppDelegate application:didFinishLaunchingWithOptions:]_block_invoke)
copy : 0x10227ddc0 (/private/var/containers/Bundle/Application/893409BD-1E16-4DBC-8D29-7DDC1355E2A5/dylibLoad.app/dylibLoad`__copy_helper_block_e8_32r)
dispose : 0x10227de04 (/private/var/containers/Bundle/Application/893409BD-1E16-4DBC-8D29-7DDC1355E2A5/dylibLoad.app/dylibLoad`__destroy_helper_block_e8_32r)
这个效果只有在IOS13系统上打印blcok才有的。
signature就是这个invoke(回调函数)返回值和参数列表的TypeEncode.
invoke就是这个回调函数所在文件的名字和地址,如果回调方法符号没被才会显示正确的方法名,不然就是内存地址。
copy 和dispose 不知,不做研究。
后来经过查看,在其父类NSBlock类上重写了-(void)debugDescription;方法。
汇编代码如下:
这里面包含了一个Block_signature函数。获取方法返回值和参数列表,汇编代码:
翻译成代码如下:
NSString * getBlockDebugDescription(struct CDBlock *blcok) {
NSMutableString *debugDescription = [NSMutableString stringWithFormat:@"\n<%@: %p>\n",blcok->isa,blcok];
NSString *block_signatur_string = getBlock_signature(blcok);
[debugDescription appendFormat:@" signature: %@\n",block_signatur_string];
Dl_info invokeInfo = {0};
if(dladdr(blcok->funk, &invokeInfo) != 0) {
[debugDescription appendFormat:@" invoke : %p\n (%s`%s offset:%p)\n",invokeInfo.dli_saddr,invokeInfo.dli_fname,invokeInfo.dli_sname,invokeInfo.dli_saddr - invokeInfo.dli_fbase];
}
Dl_info copyInfo = {0};
if(dladdr(blcok->descriptor->copy, ©Info) != 0) {
[debugDescription appendFormat:@" copy : %p\n (%s`%s offset:%p)\n",copyInfo.dli_saddr,copyInfo.dli_fname,copyInfo.dli_sname,copyInfo.dli_saddr - copyInfo.dli_fbase];
}
Dl_info disposeInfo = {0};
if(dladdr(blcok->descriptor->dispose, &disposeInfo) != 0) {
[debugDescription appendFormat:@" dispose : %p\n (%s`%s offset:%p)",disposeInfo.dli_saddr,disposeInfo.dli_fname,disposeInfo.dli_sname,disposeInfo.dli_saddr - disposeInfo.dli_fbase];
}
return debugDescription;
}
NSString* getBlock_signature(struct CDBlock *blcok) {
NSString *signature = nil;
int32_t pp11 = 0x2000000&blcok->flags; //此处已经判断是哪种descriptor了,
//CDBlockDescriptor signature 偏移0x20。
//CDBlockDescriptor1 signature 偏移 0x10
//不知道在外面在获取copy和dispose为什么还要用dladdr来判断
//终于看到flags的用处了
if (pp11 != 0) {
signature = [NSString stringWithCString:blcok->descriptor->signature encoding:NSUTF8StringEncoding];
} else {
struct CDBlockDescriptor1 *tmp = blcok->descriptor;
signature = [NSString stringWithCString:tmp->signature encoding:NSUTF8StringEncoding];
}
return signature;
}
根据以前block总结。可以得出blcok的结构体。
descriptor目前我遇到两种。
一个是CDBlockDescriptor1这种,这种是没有捕获变量的情况发。
另一种是CDBlockDescriptor,捕获了外面的变量
struct CDBlockDescriptor {
unsigned long int reserved; //8 offset:0x0
unsigned long int Block_size;//8 0x8
void * copy; //8 0x10 copy
void * dispose;//8 0x18 dispose
char * signature;//8 0x20 signature
};
struct CDBlockDescriptor1 {
unsigned long int reserved; //8 offset:0x0
unsigned long int Block_size;//8 0x8
char * signature; //8 0x10 signature
};
struct CDBlock {
Class isa; // 0 offset:0x0
int32_t flags; //4 0x4
int32_t reserved;//4 0x8
void *funk;// 8 0x10
struct CDBlockDescriptor *descriptor;// 8 0x18
//捕获的变量,....
};
翻译完,终于看到flag的用处了。
flag为什么要和0x2000000做位与处理,我也不知,汇编代码是这样处理的。0x2000000可能是某个死值吧
最后打印下:效果几乎和ios13下的打印一样:
此外,我添加了,偏移量。这样可以用Hopper直接找到函数位置。
最后给出blcok的所有代码:
#ifndef CDBlockHeader_h
#define CDBlockHeader_h
struct CDBlockDescriptor {
unsigned long int reserved; //8 offset:0x0
unsigned long int Block_size;//8 0x8
void * copy; //8 0x10 copy
void * dispose;//8 0x18 dispose
char * signature;//8 0x20 signature
};
struct CDBlockDescriptor1 {
unsigned long int reserved; //8 offset:0x0
unsigned long int Block_size;//8 0x8
char * signature; //8 0x10 signature
};
struct CDBlock {
Class isa; // 0 offset:0x0
int32_t flags; //4 0x4
int32_t reserved;//4 0x8
void *funk;// 8 0x10
struct CDBlockDescriptor *descriptor;// 8 0x18
//捕获的变量,....
};
NSString * getBlockDebugDescription(struct CDBlock *blcok);
NSString* getBlock_signature(struct CDBlock *blcok);
NSString * getBlockDebugDescription(struct CDBlock *blcok) {
NSMutableString *debugDescription = [NSMutableString stringWithFormat:@"\n<%@: %p>\n",blcok->isa,blcok];
NSString *block_signatur_string = getBlock_signature(blcok);
[debugDescription appendFormat:@" signature: %@\n",block_signatur_string];
Dl_info invokeInfo = {0};
if(dladdr(blcok->funk, &invokeInfo) != 0) {
[debugDescription appendFormat:@" invoke : %p\n (%s`%s offset:%p)\n",invokeInfo.dli_saddr,invokeInfo.dli_fname,invokeInfo.dli_sname,invokeInfo.dli_saddr - invokeInfo.dli_fbase];
}
Dl_info copyInfo = {0};
if(dladdr(blcok->descriptor->copy, ©Info) != 0) {
[debugDescription appendFormat:@" copy : %p\n (%s`%s offset:%p)\n",copyInfo.dli_saddr,copyInfo.dli_fname,copyInfo.dli_sname,copyInfo.dli_saddr - copyInfo.dli_fbase];
}
Dl_info disposeInfo = {0};
if(dladdr(blcok->descriptor->dispose, &disposeInfo) != 0) {
[debugDescription appendFormat:@" dispose : %p\n (%s`%s offset:%p)",disposeInfo.dli_saddr,disposeInfo.dli_fname,disposeInfo.dli_sname,disposeInfo.dli_saddr - disposeInfo.dli_fbase];
}
return debugDescription;
}
NSString* getBlock_signature(struct CDBlock *blcok) {
NSString *signature = nil;
int32_t pp11 = 0x2000000&blcok->flags; //此处已经判断是哪种descriptor了,
//CDBlockDescriptor signature 偏移0x20。
//CDBlockDescriptor1 signature 偏移 0x10
//不知道在外面在获取copy和dispose为什么还要用dladdr来判断
//终于看到flags的用处了
if (pp11 != 0) {
signature = [NSString stringWithCString:blcok->descriptor->signature encoding:NSUTF8StringEncoding];
} else {
struct CDBlockDescriptor1 *tmp = blcok->descriptor;
signature = [NSString stringWithCString:tmp->signature encoding:NSUTF8StringEncoding];
}
return signature;
}
#endif /* CDBlockHeader_h */
测试代码:
https://github.com/LoveSVN/Block_Struct.git