iOS底层原理篇(十八) ---- Block底层原理

1.block结构
  • 先看源码:
//用于描述块对象的>标志 flags
enum {
    BLOCK_DEALLOCATING =      (0x0001),  // runtime
    BLOCK_REFCOUNT_MASK =     (0xfffe),  // runtime
    BLOCK_NEEDS_FREE =        (1 << 24), // runtime
    BLOCK_HAS_COPY_DISPOSE =  (1 << 25), // compiler
    BLOCK_HAS_CTOR =          (1 << 26), // compiler: helpers have C++ code
    BLOCK_IS_GC =             (1 << 27), // runtime
    BLOCK_IS_GLOBAL =         (1 << 28), // compiler
    BLOCK_USE_STRET =         (1 << 29), // compiler: undefined if !BLOCK_HAS_SIGNATURE
    BLOCK_HAS_SIGNATURE  =    (1 << 30), // compiler
    BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31)  // compiler
};

//block的底层结构
struct Block_layout {
    void *isa; //isa指针,8字节
    volatile int32_t flags; //上面的枚举
    int32_t reserved;
    BlockInvokeFunction invoke;//调用函数
    struct Block_descriptor_1 *descriptor;
    //descriptor:此变量为可变,根据flags来确定是否有
    //Block_descriptor_2和Block_descriptor_3,
    //通过内存偏移获取!
};

#define BLOCK_DESCRIPTOR_1 1
struct Block_descriptor_1 {
    uintptr_t reserved;
    uintptr_t size;
};

// 可选BLOCK_DESCRIPTOR_2 和 BLOCK_DESCRIPTOR_3
#define BLOCK_DESCRIPTOR_2 1
struct Block_descriptor_2 {
	//flags 为 BLOCK_HAS_COPY_DISPOSE
    BlockCopyFunction copy;
    BlockDisposeFunction dispose;
};

#define BLOCK_DESCRIPTOR_3 1
struct Block_descriptor_3 {
	//flags 为 BLOCK_HAS_SIGNATURE
    const char *signature;
    const char *layout;
};

static struct Block_descriptor_2 * _Block_descriptor_2(struct Block_layout *aBlock)
{
    if (! (aBlock->flags & BLOCK_HAS_COPY_DISPOSE)) return NULL;
    uint8_t *desc = (uint8_t *)aBlock->descriptor;
    //在Block_descriptor_1的基础上
    //内存位移Block_descriptor_1大小.获取Block_descriptor_2
    desc += sizeof(struct Block_descriptor_1);
    return (struct Block_descriptor_2 *)desc;
}

static struct Block_descriptor_3 * _Block_descriptor_3(struct Block_layout *aBlock)
{
    if (! (aBlock->flags & BLOCK_HAS_SIGNATURE)) return NULL;
    uint8_t *desc = (uint8_t *)aBlock->descriptor;
    //在Block_descriptor_1的基础上
    //内存位移Block_descriptor_1大小
    desc += sizeof(struct Block_descriptor_1);
    if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE) {
    	//如果是BLOCK_HAS_COPY_DISPOSE类型标记
    	//说明Block_descriptor_2存在,就需要再在Block_descriptor_2的基础上
    	//内存偏移Block_descriptor_2大小获取Block_descriptor_3
        desc += sizeof(struct Block_descriptor_2);
    }
    //如果没有BLOCK_HAS_COPY_DISPOSE标记,说明block中没有Block_descriptor_2
    //在Block_descriptor_1基础上偏移Block_descriptor_1大小就是Block_descriptor_3
    return (struct Block_descriptor_3 *)desc;
}

2.block签名
  • block是匿名函数,所以,他也是有签名的
- (void)viewDidLoad {
    [super viewDidLoad];
    void (^block1)(void) = ^{
        NSLog(@"WM_Block");
    };
    block1();
}
  • 现在通过汇编底层调用的逻辑去查看打印想要看到的东西
    block内存空间打印

  • 看上面打印的结果,0x50000000是flags的值,我们使用编程计算器去计算一下是不是全局的block:

      全局的标记是BLOCK_IS_GLOBAL (1 << 28); 
      1<< 28的值是268435456,转为16进制为:0x10000000;
      flags的值是0x50000000;
      flags & BLOCK_IS_GLOBAL 结果是 0x10000000,存在,所以是全局的block
      自己可以去跟踪试一下!!!
    
  • 上面Block_descriptor_2中,aBlock->flags & BLOCK_HAS_COPY_DISPOSE这个判断,此时计算之后的值为0x0,说明此时没有Block_descriptor_2.

  • 那么现在计算一下,Block_descriptor_3是否存在,aBlock->flags & BLOCK_HAS_SIGNATURE计算的值是0x40000000,不为0,存在Block_descriptor_3!

  • 现在我们打印出Block_descriptor_3结构体中的签名的值!

  • 上面的图片中,0x0000000104c68008此为descriptor结构体x/4gx打印内存空间
    block的签名

3.block copy的分析
  • block在声明的底层调用过程中,通过断点查看汇编的调用流程会看到,都会调用一个方法_Block_copy: 去看一下这个方法的源码实现:
void *_Block_copy(const void *arg) {

    struct Block_layout *aBlock;

	//传进来的block对象 arg为空直接返回
    if (!arg) return NULL;
    
    aBlock = (struct Block_layout *)arg;
    if (aBlock->flags & BLOCK_NEEDS_FREE) {//可释放的临时的变量block,不进行copy
        latching_incr_int(&aBlock->flags);
        return aBlock;
    } else if (aBlock->flags & BLOCK_IS_GLOBAL) {//全局的,直接返回,不进行copy
        return aBlock;
    } else {
        // Its a stack block.  Make a copy.
        //arg是栈block,则进行copy
        //申请堆空间
        struct Block_layout *result =
            (struct Block_layout *)malloc(aBlock->descriptor->size);
        if (!result) return NULL;
        //copy
        memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
#if __has_feature(ptrauth_calls)
		//invoke调用的copy
        result->invoke = aBlock->invoke;
#endif

        result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING);
        result->flags |= BLOCK_NEEDS_FREE | 2;  
        _Block_call_copy_helper(result, aBlock);
        // 设置isa,一个新的初始化对象
        result->isa = _NSConcreteMallocBlock;
        return result;
    }
}
  • 看这个源码感觉没什么东西,真的这么简单吗?现在搞一个block的代码去看看copy过程是怎么样的!
#import <UIKit/UIKit.h>
#import "AppDelegate.h"

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
        
        __block NSString *name = [NSString stringWithFormat:@"iOS"];
        void (^block)(void) = ^{
            name = @"iOS_Qiao";
            NSLog(@"Block---%@",name);
        };
        block();
        
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
  • 有这样一段代码,__block修饰的name变量,在block内部我们修改打印name!我们现在对main.m在终端 xcrun -sdk iphonesimulator clang -rewrite-objc main.m,获取main.cpp文件,拿出需要研究的代码:

static void __Block_byref_id_object_copy_131(void *dst, void *src) {
 _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
static void __Block_byref_id_object_dispose_131(void *src) {
 _Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}

//__block修饰的变量被转换为__Block_byref_name_0类型的结构体 注释为下面main函数中调用时传入的值
/*
__Block_byref_name_0 继承自 Block_byref 类型
struct Block_byref {
    void *isa;
    struct Block_byref *forwarding;
    volatile int32_t flags; // contains ref count
    uint32_t size;
};

struct Block_byref_2 {
    // requires BLOCK_BYREF_HAS_COPY_DISPOSE
    //Block_byref_2 里面的这两个函数即是下面两个,这两个函数调用的时机下面会看到!!!
    //void (*__Block_byref_id_object_copy)(void*, void*);
 	//void (*__Block_byref_id_object_dispose)(void*);
    BlockByrefKeepFunction byref_keep;
    BlockByrefDestroyFunction byref_destroy;
};

struct Block_byref_3 {
    // requires BLOCK_BYREF_LAYOUT_EXTENDED
    const char *layout;
};
*/
struct __Block_byref_name_0 {
  void *__isa;//(void*)0 8字节
__Block_byref_name_0 *__forwarding;//(__Block_byref_name_0 *)&name  指针8字节
 int __flags;//33554432 转换为16进制0x2000000 4字节 
 int __size;//sizeof(__Block_byref_name_0) 4字节
 void (*__Block_byref_id_object_copy)(void*, void*);//8字节  __Block_byref_id_object_copy_131赋值给了byref_keep
 void (*__Block_byref_id_object_dispose)(void*);//8字节  __Block_byref_id_object_dispose_131赋值给了byref_destroy
 //这么长的处理是对name的处理 ((NSString * _Nonnull (*)(id, SEL, NSString * _Nonnull, ...))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("stringWithFormat:"), (NSString *)&__NSConstantStringImpl__var_folders_58_ddmrvycn42v_lhzvwvt2w9qh0000gn_T_main_95b9ff_mi_0)
 //到这里是40字节 有用哦 会通过内存偏移找到name
 NSString *name;
};

//这个是block的本质,结构体
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_name_0 *name; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_name_0 *_name, int flags=0) : name(_name->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
//block的调用
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_name_0 *name = __cself->name; // bound by ref

            (name->__forwarding->name) = (NSString *)&__NSConstantStringImpl__var_folders_58_ddmrvycn42v_lhzvwvt2w9qh0000gn_T_main_95b9ff_mi_1;
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_58_ddmrvycn42v_lhzvwvt2w9qh0000gn_T_main_95b9ff_mi_2,(name->__forwarding->name));
        }
//这两个是block的copy和dispose,很重要哦
//这两个静态的函数被传到了__main_block_desc_0_DATA 即__main_block_desc_0
//赋值给了这两个属性:
//void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
//void (*dispose)(struct __main_block_impl_0*);
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->name, (void*)src->name, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->name, 8/*BLOCK_FIELD_IS_BYREF*/);}

//__main_block_desc_0_DATA,在声明block时,传递给了struct __main_block_desc_0 *desc
static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        appDelegateClassName = NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class")));

		//此处代码即是__block修饰的name变量转换为c++代码的处理
		//可以看到两个函数:
		//一个copy:  __Block_byref_id_object_copy_131(此代码块开头)
		//一个dispose:  __Block_byref_id_object_dispose_131(此代码块开头)
		//__block修饰的变量被转换为__Block_byref_name_0类型的结构体
        __attribute__((__blocks__(byref))) __Block_byref_name_0 name = {
        														(void*)0,
        								   (__Block_byref_name_0 *)&name, 
        														33554432, 
        									sizeof(__Block_byref_name_0), 
        								__Block_byref_id_object_copy_131,
        							 __Block_byref_id_object_dispose_131, 
        							//name变量的处理
        							((NSString * _Nonnull (*)(id, SEL, NSString * _Nonnull, ...))(void *)objc_msgSend)((id)objc_getClass("NSString"), 
        							sel_registerName("stringWithFormat:"), 
        							(NSString *)&__NSConstantStringImpl__var_folders_58_ddmrvycn42v_lhzvwvt2w9qh0000gn_T_main_95b9ff_mi_0)
        };
        //block的声明
        void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_name_0 *)&name, 570425344));
        //block的调用
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

    }
    return UIApplicationMain(argc, argv, __null, appDelegateClassName);
}
  • 在上面的代码中,有两处函数的赋值很关键,其一:在处理name变量中,__Block_byref_id_object_copy_131和__Block_byref_id_object_dispose_131的赋值,是对name的处理;其二:在block声明中传入的__main_block_desc_0_DATA中,两个静态的__main_block_copy_0和__main_block_dispose_0的赋值!在这四个方法中,有关copy的函数方法中调用了_Block_object_assign,有关dispose的方法中调用了_Block_object_dispose,这两个方法很关键!
  • 以上都是block有关的一些函数赋值及声明,我们来研究一下他的调用的过程:
  • 首先来看一下_Block_object_assign和_Block_object_dispose,即copy和dispose生成生成的标记:
enum {
    BLOCK_FIELD_IS_OBJECT   =  3,  //对象
    BLOCK_FIELD_IS_BLOCK    =  7,  //block变量
    BLOCK_FIELD_IS_BYREF    =  8,  //__block修饰的结构体
    BLOCK_FIELD_IS_WEAK     = 16,  //__weak修饰的变量
    BLOCK_BYREF_CALLER      = 128, //处理Block_byref内部对象内存的时候会额外加一个标记,配合上面的枚举一起使用
};

enum {
    BLOCK_ALL_COPY_DISPOSE_FLAGS = 
        BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_BLOCK | BLOCK_FIELD_IS_BYREF |
        BLOCK_FIELD_IS_WEAK | BLOCK_BYREF_CALLER
};
//捕获外部变量时,根据类型给与不同的标记
  • 下面看一下_Block_object_assign源码实现
//block声明的时候会调用这个函数 捕获__block修饰的变量,会走到case BLOCK_FIELD_IS_BYREF
void _Block_object_assign(void *destArg, const void *object, const int flags) {
    const void **dest = (const void **)destArg;
    switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
      case BLOCK_FIELD_IS_OBJECT:
        _Block_retain_object(object);
        *dest = object;
        break;

      case BLOCK_FIELD_IS_BLOCK:
        *dest = _Block_copy(object);
        break;
    
      //此时会走到这里 调用_Block_byref_copy
      case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
      case BLOCK_FIELD_IS_BYREF:
        *dest = _Block_byref_copy(object);
        break;
        
      case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
      case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:
        *dest = object;
        break;

      case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK:
      case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK  | BLOCK_FIELD_IS_WEAK:
        *dest = object;
        break;

      default:
        break;
    }
}
  • _Block_byref_copy的处理:
static struct Block_byref *_Block_byref_copy(const void *arg) {
	//Block_byref 类型的
    struct Block_byref *src = (struct Block_byref *)arg;
    
    if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
        //栈区需要copy,申请src->size大小的内存
        struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
        //isa指针赋值
        copy->isa = NULL;
        //flags设置标记
        copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4;
        //__block修饰的属性具有修改能力的原因,就是下面的这两行代码并且原来属性的指针地址会变成拷贝后的堆地址
        copy->forwarding = copy;
        src->forwarding = copy; 
        copy->size = src->size;

        if (src->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
            struct Block_byref_2 *src2 = (struct Block_byref_2 *)(src+1);
            struct Block_byref_2 *copy2 = (struct Block_byref_2 *)(copy+1);
            //是BLOCK_BYREF_HAS_COPY_DISPOSE,获取到Block_byref_2,并对新copy的byref_keep和byref_destroy赋值
            copy2->byref_keep = src2->byref_keep;
            copy2->byref_destroy = src2->byref_destroy;

            if (src->flags & BLOCK_BYREF_LAYOUT_EXTENDED) {
                struct Block_byref_3 *src3 = (struct Block_byref_3 *)(src2+1);
                struct Block_byref_3 *copy3 = (struct Block_byref_3*)(copy2+1);
                copy3->layout = src3->layout;
            }
			//此处调用byref_keep,在上面我们说过,__Block_byref_id_object_copy_131函数赋值给了byref_keep
            (*src2->byref_keep)(copy, src);
        } else {
            memmove(copy+1, src+1, src->size - sizeof(*src));
        }
    } else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
        latching_incr_int(&src->forwarding->flags);
    }
    return src->forwarding;
}
//再次看一下__Block_byref_id_object_copy_131函数
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
	//dst是传进来的Block_byref类型的
	//这里+40是为了获取Block_byref中存储的name属性
	//为什么加40?
	//还记得上面__Block_byref_name_0j结构体中字节数的计算吗?
	//获取到name正好需要偏移40字节大小!!!!!
	_Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
//这里再次进入_Block_object_assign进行copy操作!
//但是此时进去的是BLOCK_FIELD_IS_OBJECT类型,会走到下面这个case里面:
case BLOCK_FIELD_IS_OBJECT:
       _Block_retain_object(object);
       *dest = object;
 	break;
//会调用下面的函数,这个函数是个空实现,因为是ARC,内存管理不需要处理,所以我们不再需要对此对象进行任何操作!
static void (*_Block_retain_object)(const void *ptr) = _Block_retain_object_default;
static void _Block_retain_object_default(const void *ptr __unused) { }
  • 这个就是copy的过程!
  • 那么dispose就相对容易了,在__main_block_dispose_0中调用_Block_object_dispose方法:
void _Block_object_dispose(const void *object, const int flags) {
    switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
      case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
      case BLOCK_FIELD_IS_BYREF:
       	//__block修饰的释放过程 ,重点我们看这个
        _Block_byref_release(object);
        break;
      case BLOCK_FIELD_IS_BLOCK:
      	//对捕获block类型的释放
      	/*
      	void _Block_release(const void *arg) {
    		struct Block_layout *aBlock = (struct Block_layout *)arg;
    		if (!aBlock) return;
    		if (aBlock->flags & BLOCK_IS_GLOBAL) return;
    		if (! (aBlock->flags & BLOCK_NEEDS_FREE)) return;

    		if (latching_decr_int_should_deallocate(&aBlock->flags)) {
        		_Block_call_dispose_helper(aBlock);
        		_Block_destructInstance(aBlock);
        		free(aBlock);
    		}
		}
      	*/
        _Block_release(object);
        break;
      case BLOCK_FIELD_IS_OBJECT:
      	//直接调用释放
      	/*
      	空实现,交给了ARC
      	static void (*_Block_release_object)(const void *ptr) = _Block_release_object_default;
      	static void _Block_release_object_default(const void *ptr __unused) { }
		*/
        _Block_release_object(object);
        break;
      case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
      case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:
      case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK:
      case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK  | BLOCK_FIELD_IS_WEAK:
        break;
      default:
        break;
    }
}
//__block修饰的变量的释放
static void _Block_byref_release(const void *arg) {
    struct Block_byref *byref = (struct Block_byref *)arg;
    byref = byref->forwarding;
    if (byref->flags & BLOCK_BYREF_NEEDS_FREE) {
        int32_t refcount = byref->flags & BLOCK_REFCOUNT_MASK;
        os_assert(refcount);
        if (latching_decr_int_should_deallocate(&byref->flags)) {
            if (byref->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
                struct Block_byref_2 *byref2 = (struct Block_byref_2 *)(byref+1);
                //判断满足释放条件
                //这里思路和copy一样的,
                //一层一层的copy,释放的时候也是逐层释放
                (*byref2->byref_destroy)(byref);
            }
            free(byref);
        }
    }
}
  • 这就是dispose,相对来说简单一点!!!
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值