block在项目的开发中使用时非常频繁的,苹果官方也极力推荐使用block。其实,究其本质,block就是指向结构体的指针(可利用运行时机制查看底层生成的c代码)。然而在使用block时会存在很多陷阱(主要是内存泄露),这些都是必须要注意的。接下来举个简单的实例:
假设一个类,拥有两个属性:block和name;
1 //
2 // SZBlocTest.h
3 // block引发的内存泄漏
4 //
5 // Created by mac on 14-6-17.
6 // Copyright (c) 2014年 shunzi. All rights reserved.
7 //
8
9 #import <Foundation/Foundation.h>
10
11 @interface SZBlocTest : NSObject
12
13 @property (nonatomic, copy) void(^block)();
14
15 @property (nonatomic, copy) NSString *name;
16
17 @end
在这之前,先解释下block为什么使用copy来修饰?其主要原因如下:
在普通情况下,任何block,都是存在于栈中,这也意味着,其生命周期由系统管理,不需要我们手动管理.这就存在一个问题,那就是我们如果使用block保存一段代码块,欢乐的等待被回调的时候,说不定在之前就已经被系统回收了!
那么,如何让block存储到堆中呢?方法就是使用copy来修饰,做一次复制.(如果使用retain的话,只会将其计数器加一,多做一次强引用,但不会重新分配新的内存,所以,依然存在于栈中).
此时,产生第二个问题:如果在block中调用持有它的对象(有点绕,理解下),就会产生循环引用,造成内存泄漏!例如:
1 //
2 // SZBlocTest.m
3 // block引发的内存泄漏
4 //
5 // Created by mac on 14-6-17.
6 // Copyright (c) 2014年 shunzi. All rights reserved.
7 //
8 #import "SZBlocTest.h"
9
10 @implementation SZBlocTest
11 - (id)init
12 {
13 if (self = [super init]) {
14 self.block = ^{
15 NSLog(@"%@",_name);
16 NSLog(@"%@",self->_name);
17 NSLog(@"%@",self.name);
18 };
19 }
20 return self;
21 }
22 @end
以上代码中,block中使用的
15 NSLog(@"%@",_name);
16 NSLog(@"%@",self->_name);
17 NSLog(@"%@",self.name);
都会引发循环引用,其中15与16行,是等同的;为什么会引发循环引用呢?
因为,block存在于堆中,在其代码块中引用的对象都会产生一个強指针.而这时候问题就产生了,因为block本身就被其引用的对象(copy)强指针指向着.这样就造成了双方都无法释放,从而造成了内存泄漏.
解决方案如下:
1 - (id)init
2 {
3 if (self = [super init]) {
4 __unsafe_unretained SZBlocTest *temp = self;
5 // __weak SZBlocTest *temp = self;
6 self.block = ^{
7 NSLog(@"%@",temp->_name);
8 NSLog(@"%@",temp.name);
9 };
10 }
11 return self;
12 }
这时候,很多细心的人就会注意到,第五行的被注释的代码
__weak SZBlocTest *temp = self;
和上面的
__unsafe_unretained SZBlocTest *temp = self;
有什么区别呢?
简单说就是,unsafe,为什么不安全呢?因为当其所修饰的对象释放时,它并不知道,所以,其地址依然存在,不会清为nil,这样第七行代码
temp->_name
是可以访问的(在使用时也应当注意这个细节),而使用weak修饰的话,当其修饰的对象被释放时,temp会被清为nil,这样就会出现nil->_name的情况.这是不允许的.
如果想要了解
__unsafe_unretained和__weak的具体区别的话,可查阅其它资料.
block引发的陷阱,布布扣,bubuko.com
时间: 2024-10-07 20:12:47