block资深解读【精品】

block也叫匿名函数,也叫闭包,很多语言都有闭包的概念,block是c语言的闭包演化而来
block的格式一般是(void)(^blockname)(NSString *param); 或者 ^(void)(NSString *param){}
block作为参数,property....http://fuckingblocksyntax.com/

block的内存
block有三种,分别存于不同的内存域,分别是NSGlobalBlock,NSStackBlock, NSMallocBlock
默认是在NSGlobalBlock比如下面的形式,因为他初始化在栈上,而没有与其他内存发生任何交互,所以当代码结束时,block自动释放了,跟一个方法体没有区别,最后OC把他放到TEXT端。
BlkSum blk1 = ^ long (int a, int b) {
return a + b;
};
NSLog(@"blk1 = %@", blk1);

第二种是NSStackBlock,这个是在栈上的内存,block创建时在栈上,并且用到了栈上定义的其他内存,所以这类block暂时在栈上,对外部变量也只有读的权限,没有写权限,因为这个时候只是copy了一份局部变量的值到栈上
int base = 100;
BlkSum blk2 = ^ long (int a, int b) {
return base + a + b;
};
NSLog(@"blk2 = %@", blk2); // blk2 = <NSStackBlock: 0xbfffddf8>

第三种是NSMallocBlock ,这个block是把栈上的block copy到了堆上
BlkSum blk3 = [[blk2 copy] autorelease];
NSLog(@"blk3 = %@", blk3); // blk3 = <NSMallocBlock: 0x902fda0>

然而变量都有其作用域,那么跨作用域的变量值又怎么被block调用和修改呢呢?
使用__block修饰符修改变量的作用域,而不是copy一份到栈上供block使用,
对于static静态变量,由于其地址是固定的,所以block可以对其进行修改,__block修改基本类型的局部变量等效与静态变量
@property (nonatomic, copy) void(^myBlock)(void);
MyClass* obj = [[[MyClass alloc] init] autorelease];
self.myBlock = ^ { [obj doSomething]; };
这个主意几个点,
1,定义block属性需要用copy,这样会把block对象从堆上移动到栈内存上,
2,block引用了obj会让obj的内存引用计数加1

block很容易出现retain cycle的,下面是常见的场景之一,reques引用了block,而block里面又持有request的引用,很多时候我们在self中copy了一个block,在block中又出现调用self的情况,这个时候就出现了retain cycle,解决办法是使用__block或者__weak打断强引用即可,其实以前争论的该用__block还是__weak其实是没有结果的,因为他们都能打断强引用,只要阻止了在block中调用外部OC对象并让引用计数增加就行了。出现retaincycle的情况不局限于两个object,有时候可能涉及到很多的Object,那么解决办法也是同理的,只要知道block调用外部OC变量会使其引用计数加1,还要遵守在block中调用外部变量时打断强引用就行了。
ASIHTTPRequest request = [ASIHTTPRequest requestWithURL:url];
[request setCompletionBlock:^{
NSString
string = [request responseString];
}];
或者:
elf.myBlock = ^ { [self doSomething]; };

还有个非常大的神坑,这次不会retaincycle,而是会crash,在携程代码中经常看到的坑如下:请注意,dispatchBlock跟OC定义的block又是两个概念,dispatch本身是一种安全的block,在进入block时会retain一次block的持有者也就是self,但是当碰到__block参数时,就不会retainself, 而dispatch的block又不属于self的属性,所以当block的任务还没有完成这个时候self已经释放了,那么dispatch的block就挂了,正常使用即可,但是比较坑的是,很多秀才在碰到block的地方就用__block,将Block作为参数传给dispatch_async时,系统会将Block拷贝到堆上,如果Block中使用了实例变量,还将retain self,因为dispatch_async并不知道self会在什么时候被释放,为了确保系统调度执行Block中的任务时self没有被意外释放掉,dispatch_async必须自己retain一次self,任务完成后再release self。但这里使用__block,使dispatch_async没有增加self的引用计数,这使得在系统在调度执行Block之前,self可能已被销毁,但系统并不知道这个情况,导致Block被调度执行时self已经被释放导致crash
__block kkProducView* weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.xx = xx;
});

防止retainCycle总结:
真理:
1,block引用外部基本数据类型使用__block.
2,block引用外部对象使用__weak.
3,block作为某对象的成员变量时使用copy,把block的内存复制到对上.//要不然block跑一遍就释放了

改正:
1,针对retainCycle问题,在arc之前无论对象还是基本数据类型用__block就不会错,但是到了arc的世界,一切都变了,arc下对象默认是strong的,那么在block里面调用__block修饰的对象会使对象的引用计数加1,如果不用__block修饰也会加1,使用__weak修饰就不会加1了,因为__weak把对象转成weak的,在block里面调用外部对象,引用计数不会加1.----理由:http://wufawei.com/2013/06/block-retain-cycle/

二,block结构体
struct
Block_literal_1 {

void
*isa;

int
flags;

int reserved;
void (*invoke)(void *, …);//是一个函数指针,它指向的是Block被转换成函数的地址
struct
Block_descriptor_1 {

unsigned long int
reserved;

unsigned long int
size;

// optional helper functions

void (*copy_helper)(void *dst, void *src); // IFF (1<<25)

void (*dispose_helper)(void *src); // IFF (1<<25)

// required ABI.2014.5.25

const char *signature; // IFF (1<<30)

     } *descriptor;
// imported variables

};
1,isa:证明block是一个OC特性的对象,isa指针指向的是对象的地址,这个地址有两类,分别表示了存放在两种内存的block,
_NSConcreteStackBlock/_NSConcreteGlobalBlock在没有开启ARC的情况下,如果Block中包含有局部变量则isa被初始化为前者,
否则就被初始化为后者。而当ARC开启后,如果Block中包含有局部变量则isa被初始化为 _NSConcreteMallocBlock ,否则就被初始化为 _NSConcreteGlobalBlock
2,invoke是一个函数指针,它指向的是我自己Block被转换成函数的地址
3,最后的imported variables部分是Block需要访问的外部的局部变量,他们在编译就会被拷贝到Block中,这样一来Block就是成为一个闭包了
4,闭包的解释:闭包是可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)

阅读参考:
http://tanqisen.github.io/blog/2013/04/19/gcd-block-cycle-retain/ 正确使用block

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

推荐阅读更多精彩内容

  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,036评论 29 470
  • ———————————————回答好下面的足够了---------------------------------...
    恒爱DE问候阅读 1,673评论 0 4
  • 多线程、特别是NSOperation 和 GCD 的内部原理。运行时机制的原理和运用场景。SDWebImage的原...
    LZM轮回阅读 1,982评论 0 12
  • iOS面试小贴士 ———————————————回答好下面的足够了------------------------...
    不言不爱阅读 1,876评论 0 7
  • 《Objective-C高级编程》这本书就讲了三个东西:自动引用计数、block、GCD,偏向于从原理上对这些内容...
    WeiHing阅读 9,732评论 10 69