zoukankan      html  css  js  c++  java
  • 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的具体区别的话,可查阅其它资料.



  • 相关阅读:
    Windows 命令行
    建议博客园提供 BT 资源发布功能——由分享 VS2005 Beta2 的历程想到的
    成功 自信 快乐(转载)
    轻松一下,加菲猫语录
    COmega 概述
    对 RBAC 几种模型的理解及初步实现构想
    DHTML 中滚动条的设置
    求婚
    终于有了自己的blog!
    VS2005 已发布!
  • 原文地址:https://www.cnblogs.com/dashunzi/p/3768563.html
Copyright © 2011-2022 走看看