objective-c Block实质

一. Block本质
用一句话描述就是Block是带有自动变量值的匿名函数,举一个简单的例子
void(^blk)(void) = { printf(“Block\n”)};
blk();
系统底层是这样实现的
1. 创建一个结构体(命名为main_block_imp);
struct main_Block_imp{
void *FunctPtr; // 函数指针
…..(含有其他一些成员变量)
_main_Block_imp(void *func…….){
//构造函数
}
}
2. 定义一个静态函数:(包含要执行的代码块)
如 static void main_block_func(struct main_Block_imp *_cSelf){
printf(“Block\n”);
}
3.再 实例化一个结构体变量
main_block_imp temp = _main_Block_imp(main_block_func); 将上面的静态函数以参数的形式传入
4. 创建自动变量
创建结构体实例指针void(^blk)(void),
blk = & temp;
5. 执行blk();
*blk->FunctPtr(blk)

二 截获自动变量值
int val = 10;
void(^blk)(void) = { printf(“val = %d”, val);};
blk();
1. 系统自动创建一个静态函数
static void main_block_func(struct main_Block_imp *_cSelf){
int val = _cSelf -> val;
printf(“val = %d”, val);
}
2. 创建一个结构体实例
struct main_Block_imp{
void *FunctPtr; // 函数指针
int val;
…..(含有其他一些成员变量)
_main_Block_imp(void *func, int tempVal …….) : (val(tempVal)){
//构造函数
}
3. 原代码void(^blk)(void) = { printf(“val = %d”, val);};
void(*blk)(void) = &_main_Block_imp(main_block_func, val);
4. 原代码blk();
blk->FunctPtr(blk);
总结:也就是说变量val是按值传递的方式复制到带有自动变量的结构体struct main_Block_imp中

__block 变量

__block int val = 10;
void(^blk)(void) = { printf(“val = %d”, val);};
val = 20;
blk();


  1. __block int val = 10;
    系统会自动创建一个结构体实例
    struct __blockVal {
    __blockVal * self; (指向该实例自己的指针)
    int val; (这就是使用值,初始化为10)
    }
  2. 而上面提到的main_Block_imp将包含一个指向__blockVal实例的指针
  3. 给__block 变量赋值,就是该变量__blockVal实例的成员变量val

全局变量/全局静态变量和静态局部变量在block中的使用

这些变量在block中使用和平常使用一致,实际在block中传递的是该变量的指针

block变量和对象

代码:__block id obj = [[NSObject alloc]init];实际上就等于:
__block id __strong obj = [[NSObject alloc]init];

系统转换成__block变量用结构体形式如下:
struct __Block__obj {
void *__isa;
__Block__obj *forwarding; //指向自己内存地址的指针
void (* __Block__obj__copy)(void*, void *);
void (* __Block__obj__dispose)(void*);
__strong id obj; // 对对象的强引用
}

static void __block__obj__copy(void* dst, void *src){
_block_object_assign(……..);
}
statuc void __block__obj_dispose(void *src){
_block_object_dispose(……..);
}

当Block从栈复制到堆时,使用__block__obj__copy函数,持有Block截获的对象,当堆上的Block被废弃时,使用__block__obj_dispose函数释放所持有的对象。所以如果block被复制到堆上,只有block存在,那么所截获的对象就不会释放。

那么一开始在栈上的Block什么时候会复制到堆上呢?有以下几种情况会将Block从栈上复制到堆上:

  1. 调用Block的copy实例方法
  2. Block作为函数返回值返回时
  3. 将Block赋值给附有__strong修饰符id类型的类或者Block类型成员变量时
  4. 在方法中含有usingBlock的Cocoa框架方法或者Grand Central Dispatch(GCD)的API中传递Block时。

    注意:将Block从栈上复制到堆上时是比较耗CPU的,所以尽量避免这种情况。

所以在定义@property (nonatomic, strong/copy) (void)(^my_block)(void);
block类型的属性变量时,要用copy和strong,在ARC环境下,copy和strong本质是一样的,都是把block从声明的栈中复制到堆,不然它的生命周期随着栈走,随时可能被收回

block的循环引用

示例代码:
typedef void (^blk_t)(void);
@interface Myobject : NSObject
{
blk_t _block;
}
@end

@implementation Myobjec
- (id)init
{
self = [super init];
_block = ^{NSLog{@”self = %@”, self}; };
return self;
}
- (void)dealloc
{
NSLog(@”dealloc”);
}

int main()
{
id object = [[Myobject alloc]init];
return 0;
}
该代码中的dealloc方法一定没有被调用
将Block赋值给带有__strong修饰符的成员变量的时候,block从栈上复制到堆上,block持有self对象的强引用, 而self又持有该Block,就造成了循环引用,解决方法是用__weak 修饰符

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值