iOS

block

2019-03-07  本文已影响0人  iLeooooo

O、题:block的原理是怎样?本质是什么?

答:封装了函数调用以及调用环境的OC对象


O.a、题:__block的作用是什么?有什么使用注意点?

答:__block会将修饰的变量包装成一个__Block_byref_xxx对象,使用的时候要注意内存管理。


O.b、题:block的属性修饰词为什么是copy?使用block有哪些使用注意?

答:1. block一旦没有进行copy操作,就不会在堆上。 2. 使用注意:循环引用问题


O.c、题:block在修改NSMutableArray,需不需要添加__block?

答:不需要


block的本质是什么:
  1. block本质上也是一个OC对象,它内部也有个isa指针。
  2. block是封装了函数调用以及函数调用环境的OC对象
  3. block的底层结构图如下:
block
block的类型:

block有三种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承NSBlock类型。

继承关系:

__NSGlobalBlock__ : __NSGlobalBlock : NSBlock : NSObject
  1. __NSGlobalBlock__(NSConcreteGlobalBlock)
  2. __NSStackBlock__(NSConcreteStackBlock)
  3. __NSMallocBlock__(NSConcreteMallocBlock)
三种类型的block在内存中的布局:
block布局位置
三种类型的block的区别:
block的区别
每种类型的block调用copy后的结果:
copy
全局变量:数据段
局部变量:栈段
对象:堆段
class对象:数据段
在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如一下情况:
  1. block作为函数返回值。
  2. 将block赋值给__strong指针。
  3. block作为Cocoa API中方法名含义usingBlock的方法参数
  4. block作为GCD API的方法参数
// 3
[array enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
}];
// 4
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
 });
在MRC环境下,blcok属性建议的写法:
@property (nonatomic, copy) void (^block) void;
在ARC环境下,blcok属性建议的写法:
@property (nonatomic, copy) void (^block) void;
@property (nonatomic, strong) void (^block) void;
当block内部访问了对象类型的auto变量
  1. 如果block是在栈上,将不会对auto变量产生强引用
  2. 如果block被拷贝到堆上
    • 会调用block内部的copy函数
    • copy函数内部会调用_Block_object_assign函数
    • _Block_object_assign函数会根据auto变量的修饰符(__strong,__weak, __unsafe unretained)做出相应的操作,类似于retain(形成强引用,弱引用)
  3. 如果block从堆上移除
block
__block修饰符
  1. __block可以用于解决block内部无法修改auto变量值的问题
  2. __block不能用来修饰全局变量、静态变量(static)
  3. 编译器会将__block修饰的变量包装成一个对象
__block修饰符的内存管理
  1. 当block在栈上面的时候,并不会对__block变量产生强引用
  2. 当block被copy到堆上面时:
    • 会调用block内部的copy函数
    • copy函数内部会调用_Block_object_assign函数
    • _Block_object_assign函数会对__block变量产生强引用(retain)
  3. 当block从堆上移除时:
    • 会调用block内部dispose函数
    • dispose函数内部会调用_Block_object_dispose函数
    • _Block_object_dispose函数会自动释放__block变量产生(release)
内存管理(持有)
内存管理(释放)
对象类型的auto变量,__block变量
  1. 当block在栈上的时候,都不会对他们产生强引用
  2. 当block拷贝到堆上的时候,都会通过copy函数来处理他们
    • __block变量(假设变量名为a)
    _Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
    
    • 对象类型的auto变量(假设变量名叫做p)
    _Block_object_assign((void*)&dst->p, (void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);
    
  3. 当block从堆上移除时,都会通过dispose函数来释放它们
    • __block变量(假设变量名叫做a)
    _Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
    
    • 对象类型的auto变量(假设变量名叫做p)
    _Block_object_dispose((void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);
    

内存管理
__block修饰的对象类型
  1. 当__block变量在栈上时,不会对指向的对象产生强引用

  2. 当__block变量被copy到堆时

    • 会调用__block变量内部的copy函数
    • copy函数内部会调用_Block_object_assign函数
    • _Block_object_assign函数会根据所指向对象的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用(注意:这里仅限于ARC时会retain,MRC时不会retain)
  3. 如果__block变量从堆上移除

    • 会调用__block变量内部的dispose函数
    • dispose函数内部会调用_Block_object_dispose函数
    • _Block_object_dispose函数会自动释放指向的对象(release)
循环引用
循环引用
  1. 解决循环引用问题(ARC)
    • 用__weak、__unsafe_unretained解决
    • 用__block解决(必须要调用block)


__weak : 不会产生强引用,指向的对象销毁时,会自动让指针置为nil。
__unsafe_unretained : 不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变。
  1. 解决循环引用问题(MRC)
    • 用__unsafe_unretained解决
    • 用__block解决


上一篇下一篇

猜你喜欢

热点阅读