读书笔记(二)

void (^blk) (void) = ^{pringf("block \n");};

这个block的其实就是编译成一个函数指针void (*blk) (void) = &impl_0(func_0, &desc_0)

impl_0 func_0 desc_0都是结构体,impl_0的构造函数是以func_0,desc_0型变量为参数的。

blk(); 使用block其实就是调用函数指针,(*blk->impl.FuncPtr)(blk); (func_0以impl_0为参数)

block如何截取局部变量的值的?

在编译的时候,如果block声明前有局部变量,并在block内被使用了,在impl_0的构造函数的参数列表就有这个局部变量,并且使用局部变量的值来初始化;在impl_0的成员变量也添加了这些成员变量。

在block声明前如果有静态局部变量,

static int static_val = 3;

相当于在impl_0内增加了int *static_val成员变量,使用该变量时即是使用它的指针进行访问。

为什么非静态的局部变量没有用这个方式来进行访问呢,如果这样就不存在block截获局部变量的值的情况了。因为如果局部变量被截获后(在block声明处被截获),如果该局部变量作用域结束,这个局部变量就被废弃了,block中就不能再对它访问了。

__block实现

__block int val = 0;

被编译成:(括号里面是赋的值)

struct __Block_byref_val_0{

    void *__isa;(0)

    __Block_byref_val_0 * __forwarding;(&val)

    int __flags;(0)

    int __size;(__Block_byref_val_0)

    int val;(10)

}

block内对val赋值:

^{val = 1;}

在func_0内被编译成,在impl_0中同样会添加成员变量

{

    __Block_byref_val_0 *val = __cself->val;

    val->__forwarding->val = 1;

}

Block存储域

一般block设置在上,_NSConcreteStackBlock。isa=_NSConcreteStackBlock

有两种情况在global数据区 即isa=_NSConcreteGlobalBlock:

1、block声明为全局变量

2、block中不截获局部变量的值(执行时不依赖局部变量的值,整个程序只需一个实例)

设置在栈上有个问题,就是声明block时的变量作用域结束了,栈也就没了,block和__block变量也都没了,所以arc时,大多数情况下编译器会自动生成将block从栈上复制到堆上的代码来解决这个问题。

比如下面这个将block作为函数返回值

blk_t func(int rate)

{

    return ^(int count){return rate * count;};

}

blk_t func(int rate)

{

    blk_t tmp = &_func_block_impl_0(func_0,desc_0,rate);

    tmp = _Block_copy(tmp);从栈复制到堆

    return objc_autoreleaseReturnValue(tmp); 注册到autoreleasePool并返回

}

有些情况编译器不能判断:

函数参数传递block(例外:函数里面已经复制了参数的就不用在函数前手动复制了,还有框架的某个方法名中有usingBlock,GCD的API里)

对block调用copy方法时,原来在栈上的block复制到堆上;原来在数据区的,什么也不做;原来在堆的,引用计数加1

那么block中使用的__block变量在block从栈复制到堆上又发生了什么?

同样是从栈复制到堆,并依然被该block所持有。

__block变量在从栈复制到堆上的时候,将__forwarding指向堆上的__block变量,所以不论是访问栈上的(在block外使用__block变量)还是访问堆上的(block内使用__block变量),都能通过val.__forwarding->val来访问。(访问的都是堆上的??)

block持有截获的局部变量(如果是weak类型呢??)

typedef void(^blk_t)(id obj);

blk_tblk;

{

id array = [[NSMutableArray alloc] init];

blk = [^(id b){

[array addObject:obj];

NSLog(@"array count %ld",[array count]);

}copy];

}

blk([[NSObject alloc] init]);

blk([[NSObject alloc] init]);

blk([[NSObject alloc] init]);


strong

block在编译后成员变量添加了id __strong array;

由于[block copy]的调用,多了__main_block_copy_0和__main_block_dispose_0方法,在__main_block_copy方法中调用__Block_object_assign将对象赋值给array成员变量并持有该对象,同理,__main_block_dispose_0方法中调用_Block_object_dispose释放赋值给array的对象。

所以在大括号结束时,即便array已经超过变量作用域,堆上的block依然持有该对象,不能被废弃。

block从栈赋值到堆有四种情况:

1、[block copy]

2、return block; block作为函数返回值

3、id strong blk = block; / self.blk = block;

4、xxxusingBlock(blk_t blk) /  GCD的API用block做参数的


变成weak之后 发现block结构体中的成员变量也变成weak了


改为weak
持有weak变量

array2在出变量作用域之后设为nil,所以三次输出都是0

可用声明为weak来避免循环引用。


看一下block截获基础类型变量和对象类型变量的区别

__block int  val = 10;

该变量转换为结构体__Block_byref_val_0

同时转换后的文件头部多了___main_block_copy_0___main_block_dispose_0这两个函数定义,

这两个函数内部调用了__Block_object_assign和_Block_object_dispose方法,

__Block_object_assign方法中让Block持有__block变量

这两个方法有个参数block_field_is_byref(绿色的)代表是assign了__block变量,如果是id类型,但是没有__block修饰符,则这里的值是BLOCK_FIELD_IS_OBJECT。

具体的__Block_object_assign实现是这样的:


这两个函数在main中并没有被调用,那么是何时进行了调用呢?

当block从栈复制到堆上的时候,调用__main_block_copy_0,__block变量也从栈复制到堆上时,并被Block所持有。

__block id obj = [[NSObject alloc] init];的情况

__Block_object_assign函数将__block变量赋值给block成员变量,strong类型变量持有对象,从而block持有该对象

只要__block变量在堆上存在,对象就会一直被持有。

__block id __strong obj = [[NSObject alloc] init];  和不带__strong的情况一样

总结一下就是:

ARC下,Block中引用id类型的数据有没有__block都一样都是retain,而对于基础变量如int而言,没有的话无法修改变量值,有的话就是修改其结构体令其内部的forwarding指针指向拷贝后的地址达到值的修改。而MRC下,则都是拷贝一份指针。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容