原文参考博文:
http://blog.devtang.com/2013/07/28/a-look-inside-blocks/
http://www.cnblogs.com/kesalin/archive/2013/04/30/ios_block.html
block 实际上可以理解成是Objective-C 语言对于闭包的实现。配合上 dispatch_queue,可以方便地实现简单的多线程编程和异步编程。
关于闭包,可以理解成是一个函数(或指向函数的指针),再加上该函数执行的外部的上下文变量。
这里介绍关于block的:
1,内部实现,数据结构介绍
2,block 的三种类型及其相关的内存管理方式
3,block 如何通过 capture 变量来达到访问函数外的变量
一个 block 实际是一个对象,它主要由一个 isa 和 一个 impl 和 一个 descriptor 组成。更具体的说来,结构如下:
对应的结构体定义如下,
struct Block_layout {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor *descriptor;
/* Imported variables. */
};
struct Block_descriptor {
unsigned long int reserved;
unsigned long int size;
void (*copy)(void *dst, void *src);
void (*dispose)(void *);
};
可以看出,主要包含6部分,
1,isa指针,所有对象都有这个指针。指向对象的类结构。
2,invoke指针,函数指针,指向具体的block实现的函数调用地址。
3,variables,block 能够访问它外部的局部变量,就是因为将这些变量(或变量的地址)复制到了结构体中。
4,reserved,保留字段。5,flags,descriptions,一些附加描述信息,比如size等。
OC中,一共三种类型的block,
1,_NSConcreteGlobalBlock 全局的静态 block,不会访问任何外部变量。
2,_NSConcreteStackBlock 保存在栈中的 block,当函数返回时会被销毁。
3,_NSConcreteMallocBlock 保存在堆中的 block,当引用计数为 0 时会被销毁。
block对外部变量的引用:
主要包含2类,
1,对于 block 外的变量引用,block 默认是将其复制到其数据结构中来实现访问的。
2,对于用 __block 修饰的外部变量引用,block 是复制其引用地址来实现访问的。
=======
block使用时的几个注意点:
1,block中的变量拷贝,是在实现时,对引用到的它所在方法中定义的栈变量进行一次只读拷贝。
这个变量是只读的,比如
id result = nil;
[context performBlockAndWait:^{
result = block();
}];
会报错,variable is not assignable。
需要对其有写操作时,可以加__block。
2,非内联(inline)block 不能直接访问 self。
这种情况,可以将self 当作参数传递到 block 中才能使用。而且这个时候self的属性,只能通过setter 或 getter方法访问,不能用点语法。
typedef NSString* (^IntToStringInlineConverter)(NSInteger paramInteger);
// 非内联block
IntToStringConverter independentBlockObject = ^(id self, NSInteger paramInteger) {
KSLog(@" >> self %@, memberVariable %d", self, [self memberVariable]); NSString *result = [NSString stringWithFormat:@"%d", paramInteger]; KSLog(@" >> independentBlockObject %@", result); return result; };
// 内联block,用到self则不需要传self,也可以用点语法。 - (void)testAccessSelf { // Independent [self convertIntToString:20 usingBlockObject:independentBlockObject]; // Inline IntToStringInlineConverter inlineBlockObject = ^(NSInteger paramInteger) { KSLog(@" >> self %@, memberVariable %d", self, self.memberVariable); NSString *result = [NSString stringWithFormat:@"%d", paramInteger]; KSLog(@" >> inlineBlockObject %@", result); return result; }; [self convertIntToStringInline:20 usingBlockObject:inlineBlockObject]; }
3,使用 weak–strong避免循环引用
内联block直接使用self时,需要小心,避免循环引用。
block的本质其实还是一个对象。当self强持有这个block,而这个block又强持有self时,就会导致循环引用。
换句话也就是说,只有释放了self才可以释放被self强持有的block。但self又被block强持有,无法释放。
__weak KSViewController * wself = self; _observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"TestNotificationKey" object:nil queue:nil usingBlock:^(NSNotification *n) { KSViewController * sself = wself; if (sself) { NSLog(@"%@", sself); } else { NSLog(@"<self> dealloc before we could run this code."); } }];
weak-strong的作用是,
外部传入一个self的弱引用。这样block对象并没有强持有self,当self被释放的时候,wself 变成 nil。这样已经可以解决循环引用的问题。
但是为了在block运行过程中不至于出现wself被释放,所以一般会声明一个__strong的变量,持有wself。这样,在self被释放后,block可以本正常释放,sself也就被释放。