1,block 在实现时就会对它引用到的它所在方法中定义的栈变量进行一次只读拷贝,然后在 block 块内使用该只读拷贝。堆中分配的资源例如: 函数中 内联block修改self中的参数,是可以进行修改的,函数中的栈变量 在block中 就为只读,修改此类变量,不会有影响block之外该变量的内容.如果想修改,定义:
__block NSInteger outsideVariable = 10;
2.使用使用 weak–strong 来避免循环引用
内联 block 可以直接引用 self,但是要非常小心地在 block 中引用 self。因为在一些内联 block 引用 self,可能会导致循环引用。如下例所示:
@interface KSViewController ()
{ id _observer;
}@end@implementation KSViewController- (void)viewDidLoad
{
[super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib.
KSTester * tester = [[KSTester alloc] init];
[tester run];
_observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"TestNotificationKey"
object:nil queue:nil usingBlock:^(NSNotification *n) { NSLog(@"%@", self);
}];
}- (void)dealloc{ if (_observer) {
[[NSNotificationCenter defaultCenter] removeObserver:_observer];
}
}
在上面代码中,我们添加向通知中心注册了一个观察者,然后在 dealloc 时解除该注册,一切看起来正常。但这里有两个问题.
a) 在消息通知 block 中引用到了 self,在这里 self 对象被 block retain,而 _observer 又 retain 该 block的一份拷贝,通知中心又持有 _observer。因此只要 _observer 对象还没有被解除注册,block 就会一直被通知中心持有,从而 self 就不会被释放,其 dealloc 就不会被调用。而我们却又期望在 dealloc 中通过 removeObserver 来解除注册以消除通知中心对 _observer/block 的 retain。
b) 同时,_observer 是在 self 所在类中定义赋值,因此是被 self retain 的,这样就形成了循环引用。
因此,通知中心会拷贝 block 并持有该拷贝直到解除 _observer 的注册。在 ARC 中,在被拷贝的 block 中无论是直接引用 self 还是通过引用 self 的成员变量间接引用 self,该 block 都会 retain self。
使用
_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.");
}
}];
首先,在 block 之前定义对 self 的一个弱引用 wself,因为是弱引用,所以当 self 被释放时 wself 会变为 nil;然后在 block 中引用该弱应用,考虑到多线程情况,通过使用强引用 sself 来引用该弱引用,这时如果 self 不为 nil 就会 retain self,以防止在后面的使用过程中 self 被释放;然后在之后的 block 块中使用该强引用 sself,注意在使用前要对 sself 进行了 nil 检测,因为多线程环境下在用弱引用 wself 对强引用 sself 赋值时,弱引用 wself 可能已经为 nil 了。
通过这种手法,block 就不会持有 self 的引用,从而打破了循环引用。
3.block 内存
- (id) getBlockArray
{ int val = 10; return [[NSArray alloc] initWithObjects: ^{ KSLog(@" > block 0:%d", val); }, // block on the stack
^{ KSLog(@" > block 1:%d", val); }, // block on the stack nil];
// return [[NSArray alloc] initWithObjects:// [^{ KSLog(@" > block 0:%d", val); } copy], // block copy to heap// [^{ KSLog(@" > block 1:%d", val); } copy], // block copy to heap// nil];}- (void)testManageBlockMemory
{ id obj = [self getBlockArray];
typedef void (^BlockType)(void);
BlockType blockObject = (BlockType)[obj objectAtIndex:0];
blockObject();
}
执行上面的代码中,在调用 testManageBlockMemory 时,程序会 crash 掉。因为从 getBlockArray 返回的 block 是分配在 stack 上的,但超出了定义 block 所在的作用域,block 就不在了。正确的做法(被屏蔽的那段代码)是在将 block 添加到 NSArray 中时先 copy 到 heap 上,这样就可以在之后的使用中正常访问。
只摘写了 看的懂的地方,看不懂就没搬 原帖:http://www.cnblogs.com/kesalin/archive/2013/04/30/ios_block.html
http://www.molotang.com/articles/1691.html