block引发的陷阱
block在项目的开发中使用时非常频繁的,苹果官方也极力推荐使用block。其实,究其本质,block就是指向结构体的指针(可利用运行时机制查看底层生成的c代码)。然而在使用block时会存在很多陷阱(主要是内存泄露),这些都是必须要注意的。接下来举个简单的实例:
假设一个类,拥有两个属性:block和name;
//
// SZBlocTest.h
// block引发的内存泄漏
//
// Created by mac on 14-6-17.
// Copyright (c) 2014年 shunzi. All rights reserved.
// #import <Foundation/Foundation.h> @interface SZBlocTest : NSObject @property (nonatomic, copy) void(^block)(); @property (nonatomic, copy) NSString *name; @end
在这之前,先解释下block为什么使用copy来修饰?其主要原因如下:
在普通情况下,任何block,都是存在于栈中,这也意味着,其生命周期由系统管理,不需要我们手动管理.这就存在一个问题,那就是我们如果使用block保存一段代码块,欢乐的等待被回调的时候,说不定在之前就已经被系统回收了!
那么,如何让block存储到堆中呢?方法就是使用copy来修饰,做一次复制.(如果使用retain的话,只会将其计数器加一,多做一次强引用,但不会重新分配新的内存,所以,依然存在于栈中).
此时,产生第二个问题:如果在block中调用持有它的对象(有点绕,理解下),就会产生循环引用,造成内存泄漏!例如:
//
// SZBlocTest.m
// block引发的内存泄漏
//
// Created by mac on 14-6-17.
// Copyright (c) 2014年 shunzi. All rights reserved.
//
#import "SZBlocTest.h" @implementation SZBlocTest
- (id)init
{
if (self = [super init]) {
self.block = ^{
NSLog(@"%@",_name);
NSLog(@"%@",self->_name);
NSLog(@"%@",self.name);
};
}
return self;
}
@end
以上代码中,block中使用的
15 NSLog(@"%@",_name);
16 NSLog(@"%@",self->_name);
17 NSLog(@"%@",self.name); 都会引发循环引用,其中15与16行,是等同的;为什么会引发循环引用呢?
因为,block存在于堆中,在其代码块中引用的对象都会产生一个強指针.而这时候问题就产生了,因为block本身就被其引用的对象(copy)强指针指向着.这样就造成了双方都无法释放,从而造成了内存泄漏. 解决方案如下:
- (id)init
{
if (self = [super init]) {
__unsafe_unretained SZBlocTest *temp = self;
// __weak SZBlocTest *temp = self;
self.block = ^{
NSLog(@"%@",temp->_name);
NSLog(@"%@",temp.name);
};
}
return self;
}
这时候,很多细心的人就会注意到,第五行的被注释的代码
__weak SZBlocTest *temp = self;
和上面的
__unsafe_unretained SZBlocTest *temp = self;
有什么区别呢?
简单说就是,unsafe,为什么不安全呢?因为当其所修饰的对象释放时,它并不知道,所以,其地址依然存在,不会清为nil,这样第七行代码
temp->_name
是可以访问的(在使用时也应当注意这个细节),而使用weak修饰的话,当其修饰的对象被释放时,temp会被清为nil,这样就会出现nil->_name的情况.这是不允许的.
如果想要了解
__unsafe_unretained和__weak的具体区别的话,可查阅其它资料.
block引发的陷阱的更多相关文章
- ie、firefox、chrome中关于style="display:block" 引发的页面布局错乱的解决办法
ie.firefox.chrome中关于style="display:block" 引发的页面布局错乱的解决办法: table中tr 添加style="display:b ...
- for...else: 一个程序引发的陷阱
python3菜鸟教程有一段关于for循环和else搭配的代码: for n in range(2, 10): for x in range(2, n): if n % x == 0: print(n ...
- 由ASIHttpRequest里的block引发的思考
项目发http请求,现在一般的都是用的第三方开源库,当然发异步请求时我们也会写几个回调函数来进行请求返回时的处理.不过前段时间看一个朋友写的代码,里面很用block简单的实现了回调相关的部分.比如: ...
- Foundation: NSNotificationCenter
一个NSNotificationCenter对象(通知中心)提供了在程序中广播消息的机制,它实质上就是一个通知分发表.这个分发表负责维护为各个通知注册的观察者,并在通知到达时,去查找相应的观察者,将通 ...
- 自然对数e(转)
e表示增长的极限 e=limx→+∞ (1+1/x)^x≍2.71828 假设,一根竹子,第一天是1米,第二天长了1米,然后这根柱子的长度变成了2米.相当于 (1+1/1)^1.上面这个假设,如果仔细 ...
- Python 的上下文管理器是怎么设计的?
花下猫语:最近,我在看 Python 3.10 版本的更新内容时,发现有一个关于上下文管理器的小更新,然后,突然发现上下文管理器的设计 PEP 竟然还没人翻译过!于是,我断断续续花了两周时间,终于把这 ...
- C++动态内存管理与源码剖析
引言 在本篇文章中,我们主要剖析c++中的动态内存管理,包括malloc.new expression.operator new.array new和allocator内存分配方法以及对应的内存释放方 ...
- Java 进阶6 异常处理的陷阱
Java 进阶6 异常处理的陷阱 20131113 异常处理机制是 Java语言的特色之一,尤其是 Java的Checked 异常,更是体现了 Java语言的严谨性:没有完善的错误的代码根本就不会被执 ...
- block的哪些事 --- 学习笔记十
//带有自动变量值的匿名函数 //block 与 C语言函数比只有两点不同,1.没有函数名. 2.带有"^". //类似于方法,如B中的值传给A中,在B中,一. 定义 : 二. 实 ...
随机推荐
- jeecms内容显示条数
1.按照1.2.3.4.5顺序显示 <div class="index-news"> [@cms_channel id='1'] <h2><span& ...
- 抓包利器Fiddler
1).Fiddler安装 a.下载地址: http://fiddler2.com/get-fiddler b.安装:省略(下一步...下一步即可) 2).Fiddler配置 a.允许远程计算机连接Fi ...
- tomcat7登录账户配置
tomcat7和tomcat6的用户信息配置有些不一样,tomcat7中添加了manager=gui和admin-gui角色,配置参考如下: 再 tomcat 文件夹的conf文件夹中的 tomcat ...
- java连接mysql(三)
事务的四大特性(ACID) 原子性(Atomicity) 原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败.比如在同一个事务中的SQL语句,要么全部执行成功,要么全部执行 ...
- java中两个Integer类型的值相比较的问题
今天在做一个算法时,由于为了和其他人保持接口的数据类型一致,就把之前的int换为Integer,前几天测了几组数据,和之前的结果一样,但是今天在测其它数据 的时候,突然出现了一个奇怪的bug,由于之前 ...
- ZOJ 3430 Detect the Virus
传送门: Detect the Virus ...
- android加载大图片到内存
1)演示效果: 1)代码演示: 布局代码: 权限配置:
- AngularJS 的数据绑定
单向绑定(ng-bind) 和 双向绑定(ng-model) 的区别 ng-bind 单向数据绑定($scope -> view),用于数据显示,简写形式是 {{}}. 1 <span n ...
- PHP输出表格的方法
<?php function table($n) //几行几列表 { echo'<table border="1" width="500" heig ...
- CentOS 6.4下安装Oracle 11gR2
安装前须知: 内存(RAM)的最小要求是 1GB,建议 2GB 及以上. 虚拟内存 swap 建议:内存为 1GB~2GB 时建议swap大小为内存大小的 1.5 倍:内存为 2GB~16GB 时建议 ...