block引发的陷阱

  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

block引发的陷阱的相关文章

ie、firefox、chrome中关于style=&quot;display:block&quot; 引发的页面布局错乱的解决办法

ie.firefox.chrome中关于style="display:block" 引发的页面布局错乱的解决办法: table中tr 添加style="display:block" 导致页面布局错乱 对table中tr 不显示时,添加style="display:none",ie.chrome.firefox等都没有问题.但是如果想要显示某个tr,就不能使用style="display:block"了,因为,在ie下,可以正常

ios block 引发的小bug

同事反馈在使用应用的时候启动就闪退.纠结了半天,在我这里就没什么问题. 中午在模拟器上跑,整个公司没网就突然闪退了,嗯,猜的没错,是因为网络没有,然后请求http处理的时候有点问题, 1 [[SPHttpClient manager] GET:path 2 parameters:params 3 success:^(AFHTTPRequestOperation *operation, id responseObject) { 4 NSLog(@"%@",responseObject);

malloc中 heap block 的 blocksize 大小问题

heap block 引发的思考 问题背景: Implicit Free Lists Any practical allocator needs some data structure that allows it to distinguish block boundaries and to distinguish between allocated and free blocks. Most allocators embed this information in the blocks the

Foundation: NSNotificationCenter

一个NSNotificationCenter对象(通知中心)提供了在程序中广播消息的机制,它实质上就是一个通知分发表.这个分发表负责维护为各个通知注册的观察者,并在通知到达时,去查找相应的观察者,将通知转发给他们进行处理. 本文主要了整理了一下NSNotificationCenter的使用及需要注意的一些问题,并提出了一些未解决的问题,希望能在此得到解答. 获取通知中心 每个程序都会有一个默认的通知中心.为此,NSNotificationCenter提供了一个类方法来获取这个通知中心: + (N

分页系统的实际实现问题

操作系统在四个阶段要做与分页相关的工作:进程创建,缺页中断时,进程执行,进程结束时. 当在分页系统创建一个新进程时,操作系统首先要确定程序和数据在初始时有多大,并为他们创建一个页表,然后在内存中为页表分配内存空间以及初始化,当进程被换出时,页表不用留内存中.另外,操作系统要在磁盘交换区分配空间,以便在某进程交换出区时有磁盘空间可以用,操作系统还要用程序和数据对交换区进行初始化,也就是留个备份的意思,这样当新进程发生缺页中断时,可以直接调入需要的页面,某些系统直接从磁盘上的可执行文件对程序正文进行

ReactiveCocoa Documents 翻译(基于版本V2.5)

1. 基本操作(Basic Operators) 描述 ReactiveCocoa 最常用的一些操作以及使用范例. 主要是如何运用 序列(sequences) 和 信号(signals) 的流操作. 用信号实现副作用(Performing side effects with signals) 订阅(Subscription) 依赖注入(Injecting effects) 流的传输(Transforming streams) 映射(Mapping) 过滤(Filtering) 流的结合(Comb

iOS block 陷阱解析

一,前言 <深入浅出Cocoa多线程编程之block与dispatch quene> 本文源码下载:点此下载 二,block 注意事项 1,block 在实现时就会对它引用到的它所在方法中定义的栈变量进行一次只读拷贝,然后在 block 块内使用该只读拷贝. 如下代码: - (void)testAccessVariable { NSInteger outsideVariable = 10; //__block NSInteger outsideVariable = 10; NSMutableA

hdfs datanode decomission引发的block missing

最近在帮公司下线离线计算集群的机器,逐台decomiss,下到第四台机器时,cdh告警来了,丢失块大于集群的1%,算了一下集群有400多台,下线一台不应该告警,以为下线步骤有问题,点了中止下线,后来看了下其实步骤是没问题的,由于下线的机器配置比较好,所以确实这台机器上存储了集群1%以上的block,于是终止了datanode的decomission,但是集群还是出现了14个 missing block,按照常理,hdfs上的文件丢失副本只要不是所有副本全丢,过段时间hdfs都会把这些副本从其他节

iOS block从零开始

iOS block从零开始 在iOS4.0之后,block横空出世,它本身封装了一段代码并将这段代码当做变量,通过block()的方式进行回调. block的结构 先来一段简单的代码看看: void (^myBlock)(int a) = ^(int a){ NSLog(@"%zd",a); }; NSLog(@"旭宝爱吃鱼"); myBlock(999); 输出结果: 2016-05-03 11:27:18.571 block[5340:706252] 旭宝爱吃鱼