又是Block小结

之前说了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;方法。
汇编代码如下:


E5EED2D8-01A3-478E-A00F-6D52060000A9.png

这里面包含了一个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

你可能感兴趣的:(又是Block小结)