iOS 中使用Block时需要注意的retain circle
现在在ios中,block是越来越多了。自己在类中定义block对象时,需要注意block对象的使用方法,防止产生retain circle,导致内存泄露。
现在分析一下产生retain circle的原因
比如我建立了Tools类,之后 建立了一个strong 类型的 block指针 callbackBlock,作为回掉函数使用。
之后在A类中,建立了Tools类的一个对象,并用Strong指针 aTool指向这个对象,再将它的callbackBlock指针赋值,也就是写出具体的block实现。
如果我在block的实现中用到了 self 这个指针,那么会导致retain circle。
形成的circle 如下图

解决方法:不使用self指针,使用A对象的weak引用,如果必须使用self指针,那么可以再block结束处加上 aTool = nil,用来打断retain circle。
除了这种比较明显的循环引用外,还有一些不明显的,比如以下代码
[[NSNotificationCenter defaultCenter] addObserverForName:@"UIWindowDidRotateNotification" object:nil queue:nil usingBlock:^(NSNotification *note) {
if ([note.userInfo[@"UIWindowOldOrientationUserInfoKey"] intValue] >= ) {
self.navigationController.navigationBar.frame = (CGRect){, , self.view.frame.size.width, };
}
}];
这是一段在 view controller 中的代码,由于block中使用了self指针,导致这个view controller无法被释放。由于调用的是系统函数,我们并不知道为何形成了循环引用,但是这个现象给了我们一个提示:在对象内部使用block时,一定要使用 weak self 指针!
另外,即使不直接使用self,而是使用self中定义的变量也不行!(ios8,ios9,测试了,不行,但是我记得这种情况以前不会有问题啊???难道是ios7?)
比如一下代码
@interface HubPreviewViewController (){
NSURL *webViewURL;
}
调用方法如下:
[[NSNotificationCenter defaultCenter] addObserverForName:@"UIWindowDidRotateNotification" object:nil queue:nil usingBlock:^(NSNotification *note) {
NSLog(@"webview url is %@", [weakSelf valueForKey:@"webViewURL"]);//一切正常,没有循环引用
NSLog(@"webview url is %@", webViewURL); //会造成循环引用 ,为什么呢?????没有self的调用啊??
}];
我猜测一个对象内调用的block的所有权都在这个对象上,比如上面这个例子,虽然引用block时是这样写的
^(NSNotification *note) {
if ([note.userInfo[@"UIWindowOldOrientationUserInfoKey"] intValue] >= ) {
self.navigationController.navigationBar.frame = (CGRect){, , self.view.frame.size.width, };
}
}
没有看到明显的strong 类型的 block对象指针,但是实际上,系统隐含地为这个block在 view controller对象中创建了一个 strong 类型指针,用来保存具体内容,这个block 的所有权和 上面代码中的NSNotificationCenter无关,所以从notification center 里是否移除 self 这个observer 都对block的释放没有影响。
这里推荐一篇关于block的专业文章,http://blog.csdn.net/jasonblog/article/details/7756763
阅读后,开始测试,先贴上我的测试文件:
#import <Foundation/Foundation.h>
@interface Person : NSObject{
NSURL *Testurl;
}
@end
@implementation Person
- (void)test{
[[NSNotificationCenter defaultCenter] addObserverForName:@"test" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
NSURL * innerUrl = [NSURL URLWithString:@"test"];
}];
}
- (void)dealloc{
[super dealloc];
NSLog(@"dealloc........");
}
@end
int main(int argc, const char * argv[]) {
// insert code here...
printf("Hello, World!\n");
Person *person = [[Person alloc] init];
[person test];
[person release];
return ;
}
这个是person对象可以正常释放的版本,由于clang编译器的一些原因,这里没有用ARC。
在控制台输入编译命令:
clang -framework Foundation test.m -o test
得到可执行2进制文件:

拖入控制台执行:

再执行一条命令,生成可以被我们研究的cpp文件:
clang -rewrite-objc test.m
没有引用循环的版本完成了,再改写person对象无法正确释放的代码:
#import <Foundation/Foundation.h>
@interface Person : NSObject{
NSURL *Testurl;
}
@end
@implementation Person
- (void)test{
[[NSNotificationCenter defaultCenter] addObserverForName:@"test" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
Testurl = [NSURL URLWithString:@"test"];
}];
}
- (void)dealloc{
[super dealloc];
NSLog(@"dealloc........");
}
@end
int main(int argc, const char * argv[]) {
// insert code here...
printf("Hello, World!\n");
Person *person = [[Person alloc] init];
[person test];
[person release];
return ;
}
同样经过刚才的2个步奏,但是这次的控制台没有输出 dealloc 。
为了探究原因,我们看2次生成的cpp有什么不同。
没有问题的版本:
struct __Person__test_block_impl_0 {
struct __block_impl impl;
struct __Person__test_block_desc_0* Desc;
__Person__test_block_impl_0(void *fp, struct __Person__test_block_desc_0 *desc, int flags=) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
有问题的版本:
struct __Person__test_block_impl_0 {
struct __block_impl impl;
struct __Person__test_block_desc_0* Desc;
Person *self;
__Person__test_block_impl_0(void *fp, struct __Person__test_block_desc_0 *desc, Person *_self, int flags=) : self(_self) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
而且,有问题的版本还会多2个函数:
static void __Person__test_block_copy_0(struct __Person__test_block_impl_0*dst, struct __Person__test_block_impl_0*src) {_Block_object_assign((void*)&dst->self, (void*)src->self, /*BLOCK_FIELD_IS_OBJECT*/);}
static void __Person__test_block_dispose_0(struct __Person__test_block_impl_0*src) {_Block_object_dispose((void*)src->self, /*BLOCK_FIELD_IS_OBJECT*/);}
这里的self,正是Person对象,推测这2个函数就是为了释放block对person的“强引用”而存在的。可见,有问题的版本的中的block的确对person对象进行了所谓的强引用,那么又是谁对这个block进行了强引用呢?我在代码中没有找到对应的指针,后来发现是notification center:
The block to be executed when the notification is received. The block is copied by the notification center and (the copy) held until the observer registration is removed. To unregister observations, you pass the object returned by this method to removeObserver:. You must invoke removeObserver: or removeObserver:name:object: before any object specified by addObserverForName:object:queue:usingBlock: is deallocated.
又这个提示我又写了改写了代码:
#import <Foundation/Foundation.h>
@interface Person : NSObject{
NSURL *Testurl;
}
@end
@implementation Person
- (id)test{
void (^abcdTestBolck) (NSNotification * _Nonnull note) = ^ void (NSNotification * _Nonnull note){
Testurl = [NSURL URLWithString:@"test"];
};
id returnValue = [[NSNotificationCenter defaultCenter] addObserverForName:@"test" object:nil queue:nil usingBlock:abcdTestBolck];
return returnValue;
}
- (void)dealloc{
[super dealloc];
NSLog(@"dealloc........");
}
@end
int main(int argc, const char * argv[]) {
// insert code here...
printf("Hello, World!\n");
Person *person = [[Person alloc] init];
id returnedByNoti = [person test];
[[NSNotificationCenter defaultCenter] removeObserver:returnedByNoti];
[person release];
return ;
}
上面这段代码的 person对象可以正确释放!因为把 block从 notification center 中移除了,所以 person对象的强引用也就没了!
由此我们看出,block中使用 self中的属性变量,会导致block对self的强引用,因此需要注意block的释放时机,不要导致self无法被释放!
iOS 中使用Block时需要注意的retain circle的更多相关文章
- iOS中使用block进行网络请求回调
iOS中使用block进行网络请求回调 HttpRequest.h // // HttpRequest.h // UseBlockCallBack // // Created by Michael o ...
- IOS中的Block与C++11中的lambda
ios中的block 可以说是一种函数指针,但更确切的讲,其实际上其应该算是object-c对C++11中lambda的支持或者说是一个语言上的变体,其实际内容是一样的,C++的lambda我已经有简 ...
- iOS中为什么block用copy属性
1. Block的声明和线程安全Block属性的声明,首先需要用copy修饰符,因为只有copy后的Block才会在堆中,栈中的Block的生命周期是和栈绑定的,可以参考之前的文章(iOS: 非ARC ...
- iOS 中的 block 是如何持有对象的
Block 是 Objective-C 中笔者最喜欢的特性,它为 Objective-C 这门语言提供了强大的函数式编程能力,而最近苹果推出的很多新的 API 都已经开始原生的支持 block 语法, ...
- iOS开发——高级篇——iOS中为什么block用copy属性
1. Block的声明和线程安全Block属性的声明,首先需要用copy修饰符,因为只有copy后的Block才会在堆中,栈中的Block的生命周期是和栈绑定的,可以参考之前的文章(iOS: 非ARC ...
- ios 中的block应用
在这个大冬天里默默敲着键盘,勿喷.今天学习swift过程中,学习到闭包,发现闭包和oc的block中有很多的相同之处,又重新学习了一下并且学习了一些高级点的用法,内容如下: 1.block格式说明:( ...
- iOS 中的block异常
转自:iOS 知识小集 我们在调用block时,如果这个block为nil,则程序会崩溃,报类似于EXC_BAD_ACCESS(code=1, address=0xc)异常[32位下的结果,如果是64 ...
- 关于查找iOS中App路径时所要注意的一个问题
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交 ...
- iOS 中的block异常 判断block是否为空
我们在调用block时,如果这个block为nil,则程序会崩溃,报类似于EXC_BAD_ACCESS(code=1, address=0xc)异常[32位下的结果,如果是64位,则address=0 ...
随机推荐
- 知道了grunt怎么用了
第一步: //如何安装指定grunt的插件,安装后自动在当前cmd目录下新建node_modules文件夹,文件夹里面包含了下载完成的插件 npm install <module> - ...
- sql-函数avg,count,max,min,sum
常用函数 AVG (平均) COUNT (计数) MAX (最大值) MIN (最小值) SUM (总合) 运用函数的语法是: SELECT "函数名"("栏位名&qu ...
- 【CodeForces 626C】Block Towers
题意 给你n,m,如果 n个2的倍数和m个3的倍数,这n+m个数各不相同,那么求最大的数的最小值. 分析 方法1:枚举最大值为i,直到 i/2+i/3-i/6(不重复的2或3的倍数)≥n+m,并且要i ...
- 利用symbolsource/gitlink调试你的nuget包
关键字: 如何调试Nuget下载的dll? VS github 调试 参考文章: http://docs.nuget.org/create/creating-and-publishing-a-sy ...
- jquery ajax 应用返回类型是html json
jquery ajax 例子: function JudgeUserName() { $.ajax({ type:"GET&q ...
- TIME_WAIT过多
Linux系统下,TCP/IP连接断开后,会以TIME_WAIT状态保留一定的时间,然后才会释放端口.当并发请求过多的时候,就会产生大量的 TIME_WAIT状态的连接,无法及时断开的话,会占用大量的 ...
- electron加载jquery报错问题
<!-- Insert this line above script imports --> <script>if (typeof module === 'object') { ...
- windows2003安全加固
因为IIS的方便性和易用性,使它成为最受欢迎的Web服务器软件之一.但是,IIS的安全性却一直令人担忧.如何利用IIS建立一个安全的Web服务器,是很多人关心的话题.要创建一个安全可靠的Web服务器, ...
- ucenter实现原理
其实Ucenter实现同步登陆的原理就是cookie,一个应用登陆成功之后,向Ucenter传递数据(post方式),让Ucenter通知其他的应用也设置 cookie(get方式),这样用户在访问其 ...
- mysql PDO的使用
原文链接:http://www.cnblogs.com/xiaochaohuashengmi/archive/2010/08/12/1797753.html