手把手教你看懂Block

昨天看到一篇文章,那真的是直戳我的心窝啊,那就是要不要写总结,写什么,从什么开始写等。
我想还有很多iOS爬坑的小伙伴们像我一样,每当想总结一个知识点的时候,就会想那么多知名博客都总结的很详细很透彻了,自己再去总结别人写的知识,感觉low、感觉没有创新,所以就默默的选择将知识点保存到书签方便以后去看,而不是说动手去证实某个观点、加深印象。不管你是不是,反正我的内心是这样子的。
改变就从此刻开始,是时候总结表演真正的技术了,那我就从面试的内容开始。

MRC和ARC中block的存放位置

block在内存中的存放位置决定了block的类型
1.数据区或者称为代码区( __NSGlobalBlock__ )
2.栈区( __NSStackBlock__ )
3.堆区 ( __NSMallocBlock__ )

MRC环境

NSGlobalBlock

即在block代码块中没有访问外部变量(包含了局部和全局变量)

// GlobalBlock
void (^globalBlock)(void) = ^(){
        NSLog(@"GlobalBlock");
    };
NSLog(@"%@",globalBlock);
log:<__NSGlobalBlock__: 0x10a338200>

尝试对GlobalBlock进行copy、retain、release

NSLog(@"copy:%@",[globalBlock copy]);
NSLog(@"retain:%@",[globalBlock retain]);
[globalBlock release];
NSLog(@"release:%@",globalBlock);
log:copy:<__NSGlobalBlock__: 0x10a338200>
log:retain:<__NSGlobalBlock__: 0x10a338200>
log:release:<__NSGlobalBlock__: 0x10a338200>

发现对于存放在代码区的block进行操作是没有什么作用的。

NSStackBlock
// StackBlock
int a = 10;
void (^stackBlock)(void) = ^(){
    NSLog(@"%d",a);
};
NSLog(@"%@",stackBlock);
log:<__NSStackBlock__: 0x7ffee084c588>

你会可能想会不会是因为block中访问的是基本数据类型的原因,因为基本数据类型是存放在栈上
我们用NSString *来尝试:

NSString *stackStr = @"stackBlock";
void (^stackBlock)(void) = ^(){
    NSLog(@"%@",stackStr);
};
 NSLog(@"%@",stackBlock);
log:<__NSStackBlock__: 0x7ffee5495588>

不用怀疑,结果还是在栈上,这就说明在mrc环境下,无论block中访问外部的变量是什么数据类型,都不能改变block的存储位置。
那么问题来了,既然stackBlock是存储在栈上的,那他的死活就是有系统来决定,不由我们控制。
好,我们继续来折腾。block也是一个对象嘛,所以有时候我们会把它装进数组中,那我也来试试看它有什么好玩的事情发生

// 继续上面的操作
// arr是全局不可变数组
self.arr = @[stackBlock];
然后在触摸屏幕的时候,打印一下我们数组中唯一的一个StackBlock
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@"%@",self.arr[0]);
}
StackBlock的野指针错误.png

什么鬼啊,怎么就野指针了呢?
其实到这里已经很明白了,数组中存放一个对象的时候只是进行强引用,因此指针还是同一个,当执行触摸事件的时候,栈上面的block其实已经被系统干掉了,我们在读取一个指向已经释放内存的时候就会出现万恶的野指针错误。

NSMallocBlock

OK,我描述的可能不是很好,如果您能理解以上的内容,就继续折腾一下:我们不妨对stackBlock进行一次copy操作。

// 将上面的代码添加一个copy操作
self.arr = @[[stackBlock copy]];
// 这里打印的还是stackBlock
log:<__NSStackBlock__: 0x7ffee39e1578>
stackBlock的copy.png

此时,我们看看数组中的是什么情况,通过以上图片其实不难看出,copy后的stackBlock其实已经被复制到堆上了__NSMallocBlock__
好了,我们继续执行触摸事件,看到打印的结果是

<__NSMallocBlock__: 0x60400025ef30>

所以说,为什么用block的时候推荐使用copy。好了,我们带着猜疑看看rac环境中的block吧!

RAC环境

NSGlobalBlock
// block块内部不访问外部变量
void (^globalBlock)(void) = ^(){
        NSLog(@"GlobalBlock");
};
NSLog(@"%@",globalBlock);
NSLog(@"copy:%@",[globalBlock copy]);
log:<__NSGlobalBlock__: 0x10cf7b200>
log:copy:<__NSGlobalBlock__: 0x10cf7b200>

发现和mrc中没什么区别

NSStackBlock
 NSString *stackStr = @"stackBlock";
 void (^stackBlock)(void) = ^(){
        NSLog(@"%@",stackStr);
};
 NSLog(@"%@",stackBlock);
log:<__NSMallocBlock__: 0x600000443ab0>

有没有发现和mrc中的不同。没错的,在arc中,只要我们定义一个block赋值给一个strong类型的指针,那么系统就会默认的进行copy。
那怎么证实这个结论呢?
简单呀,既然你说是strong类型的指针,那我就用弱的呗

@interface BlockController ()
@property(nonatomic,weak)void(^stackBlock)(void);
@end

NSString *stackStr = @"stackBlock";
self.stackBlock = ^(){
    NSLog(@"%@",stackStr);
};
NSLog(@"%@",_stackBlock);
log:<__NSStackBlock__: 0x7ffee144b590>

是不是就变成了__NSStackBlock__了。其实还有一个更简单的验证方法,直接打印一个block。

NSString *stackStr = @"stackBlock";
NSLog(@"%@", ^(){
    NSLog(@"%@",stackStr);
});
log:<__NSStackBlock__: 0x7ffee862e590>
NSMallocBlock

其实在rac中,我们平时使用的block,绝大部分都是存在堆区,并不是说rac中block就不能存在栈区。
该知识点也是小的在面试的时候回答的不是很好,所有冲动之后记录下来,方便以后纠错。如果老铁发现有bug,请指正。感谢您的阅读。

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

推荐阅读更多精彩内容

  • 前言 Blocks是C语言的扩充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了这...
    小人不才阅读 3,759评论 0 23
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,135评论 30 470
  • 1.设计模式是什么? 你知道哪些设计模式,并简要叙述? 设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类...
    司马DE晴空阅读 1,283评论 0 7
  • 1、设计模式是什么? 你知道哪些设计模式,并简要叙述? 设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类...
    ptlCoder阅读 1,613评论 1 19
  • 设计模式是什么? 你知道哪些设计模式,并简要叙述? 设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类型的...
    不懂后悔阅读 824评论 0 53