IOS开发—block对外部变量的内存管理

block对外部变量的内存管理

代码块在ios中通常用于回调,本文主要介绍block对外部变量的管理机制。我们知道如果要在block中使用block外面的变量,如果该变量是局部变量,就要先将其申明为__block类型。为什么呢?这就涉及到block对外部变量的内存管理。

一、基本数据类型

先看下面测试代码:

//局部变量
- (void)localDataTest
{
    int localData = 100;
    NSLog(@"localData --%p",&localData);
    int (^sum)() = ^(int a,int b){
        NSLog(@"localData --%p",&localData);
        return localData + a + b;
    };
    localData = 0;
    int c = sum(1,2);
    NSLog(@"localDataTest -- c= %d",c);
}

打印结果:

2015-03-21 12:04:11.161 test1[837:31981] localData --0xbfff4ed0

2015-03-21 12:04:11.162 test1[837:31981] localData --0x7c18bb44

2015-03-21 12:04:11.162 test1[837:31981] localDataTest --c = 103

//静态变量
- (void)staticDataTest
{
    static int staticData = 100;
    NSLog(@"staticData --%p",&staticData);
    int (^sum)() = ^(int a,int b){
        NSLog(@"staticData --%p",&staticData);
        return staticData + a + b;
    };
    staticData = 0;
    int c = sum(1,2);
    NSLog(@"staticDataTest --%d",c);
}

打印结果:

2015-03-21 12:04:11.162 test1[837:31981] staticData --0xc7b8

2015-03-21 12:04:11.162 test1[837:31981] staticData --0xc7b8

2015-03-21 12:04:11.162 test1[837:31981] staticDataTest-- 3

//全局变量
- (void)globalDataTest
{
    NSLog(@"globalData --%p",&globalData);
    int (^sum)() = ^(int a,int b){
        NSLog(@"globalData --%p",&globalData);
        return globalData + a + b;
    };
    globalData = 0;
    int c = sum(1,2);
    NSLog(@"globalData --%d",c);
}

打印结果:

2015-03-21 12:04:11.162 test1[837:31981] globalData --0xc7b4

2015-03-21 12:04:11.162 test1[837:31981] globalData --0xc7b4

2015-03-21 12:04:11.163 test1[837:31981] globalData -- 3

//block变量
- (void)blockDataTest
{
    __block int blockData = 100;
    NSLog(@"blockData --%p",&blockData);
    int (^sum)() = ^(int a,int b){
        NSLog(@"blockData --%p",&blockData);
        return blockData + a + b;
    };
    blockData = 0;
    int c = sum(1,2);
    NSLog(@"blockDataTest -- c= %d",c);
}

打印结果:

2015-03-21 12:04:11.163 test1[837:31981] blockData --0xbfff4ed8

2015-03-21 12:04:11.163 test1[837:31981] blockData --0x7c2411c0

2015-03-21 12:04:11.163 test1[837:31981] blockDataTest --c = 3

上面4段代码是分别对block中用到的局部变量、静态变量、全局变量、block变量的测试。测试结果显示:

对于block中用到的外部变量,若是静态类型或者全局类型,block中该变量的地址没有发生变化。由于静态变量和全局变量其地址是固定的,因此block在定义的时候并没有复制该变量的值,而是直接从其所在内存中读出。

再来看局部(本地)变量和block变量,在block中它们两个的地址都发生了变化,第一反应是block在定义的时候拷贝了这两种类型的变量(即开辟了新的内存空间),但是两者sum()的结果却是不同的。事实上并不是都被拷贝了:

对于局部变量,block在定义的时候复制了它,它在block中是作为常量使用的,其值不受外面的影响,因此在测试代码中,blockData的值始终为100。

对于block变量,可以看到其地址发生了变化,但是blockData却受到外界影响。这是因为,blockData在定义变量本身的时候是位于stack上的,而在定义block的时候,该变量并不是被复制了一份,而是编译器将其转移到了heap上。这一点通过下面代码可以得到验证:

- (void)blockDataTest
{
    __block int blockData = 100;
    NSLog(@"blockData -- %p",&blockData);
    int (^sum)() = ^(int a,int b){
        NSLog(@"blockData -- %p",&blockData);
        return blockData + a + b;
    };
    NSLog(@"blockData -- %p",&blockData);
    blockData = 0;
    int c = sum(1,2);
    NSLog(@"blockData -- %d",c);
}

打印结果:

2015-03-21 13:22:07.350 test1[1180:68166] blockData --0xbff47ed8

2015-03-21 13:22:07.350 test1[1180:68166] blockData --0x7c452680

2015-03-21 13:22:07.350 test1[1180:68166] blockData --0x7c452680

2015-03-21 13:22:07.350test1[1180:68166] blockData – 3

可以看到block定义之后,blockData的地址就发生了变化。这样就能理解为何打印出来的blockData值是3了。

二、对象类型

//本地对象

- (void)localObjTest
{
    UILabel *localObj = [[UILabel alloc]init];
    NSLog(@"localObj adress --%p", & localObj);
    void (^test)() = ^{
        NSLog(@"localObj adress --%p", & localObj);
        NSLog(@"localObj --%@",localObj);
    };
    localObj = nil;
    test();
}

打印结果:

2015-03-21 13:44:25.848 test1[1332:79267] localObj adress-- 0xbff7ced4

2015-03-21 13:44:25.848 test1[1332:79267] localObj adress-- 0x7b63f8b4

2015-03-21 13:44:25.848 test1[1332:79267] localObj --<UILabel: 0x7c8337a0; frame = (0 0; 0 0);

//静态对象
- (void)staticObjTest
{
    static UILabel *staticObj ;
    staticObj = [[UILabel alloc]init];
    NSLog(@"staticObj adress --%p", & staticObj);
    void (^test)() = ^{
        NSLog(@"staticObj adress --%p", & staticObj);
        NSLog(@"staticObj --%@",staticObj);
    };
    staticObj = nil;
    test();
}

打印结果:

2015-03-21 13:44:25.917 test1[1332:79267] staticObjadress -- 0x85a48

2015-03-21 13:44:25.917 test1[1332:79267] staticObjadress -- 0x85a48

2015-03-21 13:44:25.917 test1[1332:79267] staticObj --(null)

//全局对象
- (void)globalObjTest
{
    globalObj = [[UILabel alloc]init];
    NSLog(@"staticObj adress --%p", & globalObj);
    void (^test)() = ^{
        NSLog(@"globalObj adress --%p", & globalObj);
        NSLog(@"globalObj --%@",globalObj);
    };
    globalObj = nil;
    test();
}

打印结果:

2015-03-21 13:44:25.917 test1[1332:79267] staticObjadress -- 0x85a4c

2015-03-21 13:44:25.917 test1[1332:79267] globalObjadress -- 0x85a4c

2015-03-21 13:44:25.917 test1[1332:79267] globalObj --(null)

//block对象
- (void)blockObjTest
{
    __block UILabel *blockObj = [[UILabel alloc]init];
    NSLog(@"blockObj adress --%p", & blockObj);
    void (^test)() = ^{
        NSLog(@"blockObj adress --%p", & blockObj);
        NSLog(@"blockObj --%@",blockObj);
    };
    blockObj = nil;
    test();
}

打印结果:

2015-03-21 13:44:25.917 test1[1332:79267] blockObj adress-- 0xbff7ced0

2015-03-21 13:44:25.918 test1[1332:79267] blockObj adress-- 0x7c835598

2015-03-21 13:44:25.918 test1[1332:79267] blockObj --(null)

总结:从测试结果可以看到

*对于静态对象变量和全局对象变量,其地址同样不是固定的。

*对于局部(本地)对象变量,定义block的时候同样复制了(指针)变量,其在block中是作为(指针)常量使用的,不会受外界影响。

*对于block对象变量,定义block的时候同样将变量从栈转移到了堆上,这一点用刚才的方法同样可以验证。因此block变量是受外界影响的,导致输出结果为空。

三、循环引用

block对于其中的任何一个对象都会形成强引用,倘若其中的某个对象同同样强引用了block,就会造成两个对象相互强引用了对方,也就是循环引用,从而造成彼此之间谁都无法释放谁的尴尬局面,也就造成了内存泄露。解决这一问题的方法是在block外面对对象做弱引用处理。以self为例:

由于self对于block是强引用,因此如果block中需要用到self,如果不对self做事先处理,就会形成循环引用。处理的方法是:在block前加这样一句:

__weak typeof(self) weakSelf = self;

这句话的作用是重新创建了一个weak类型的self对象,block对其不会形成强引用,即在引用weakSelf的时候不会对其retain,从而避免了循环引用的发生。

__block typeof(self) weakSelf= self;  

[self methodThatTakesABlock:^ {  

    [weakSelf doSomething];
}

通过下图方便理解,实箭头表示强引用,虚箭头表示弱引用。

时间: 2024-10-22 20:15:59

IOS开发—block对外部变量的内存管理的相关文章

block没那么难(二):block和变量的内存管理

本系列博文总结自<Pro Multithreading and Memory Management for iOS and OS X with ARC> 了解了 block的实现,我们接着来聊聊 block 和变量的内存管理.本文将介绍可写变量.block的内存段.__block变量的内存段等内容,看完本文会对 block 和变量的内存管理有更加清晰的认识. 上篇文章举了个例子,在 block 内获取了一个外部的局部变量,可以读取,但无法进行写入的修改操作.在 C 语言中有三种类型的变量,可在

iOS开发——修改指定文件的内存管理状态

今天项目要上线,在Archive时报错:  ARC forbids explicit message send of 'release' 'release' is unavailable: not available in automatic reference counting mode   项目中有几个第三方库用到MRC,在release时报错.在运行时没有用到这个库所以没有报错(没有确定).于是我改了那几个文件为MRC状态,成功Archive,在ARC改为-fobjc-arc,MRC为-f

【iOS开发-33】学习手动内存管理临时抛弃ARC以及retain/assign知识——iOSproject师面试必考内容

我们为什么须要内存管理?当使用内存达到40M和45M时候会发出警告,假设不处理,占用内存达到120M时直接强制关闭程序. 所以出现闪退除了是程序出现逻辑错误,还有可能是内存使用过大. (1)创建一个对象的过程:先分配内存空间存储对象:初始化成员变量:返回对象的指针. (2)对象在创建时,内部会自己主动创建一个引用计数器retainCount,当retainCount=0时,系统会回收当前对象,retainCount是唯一推断标记.release会-1.retain会+1,retain后返回的是自

【iOS开发-33】学习手动内存管理暂时抛弃ARC以及retain/assign知识——iOS工程师面试必考内容

我们为什么需要内存管理?当使用内存达到40M和45M时候会发出警告,如果不处理,占用内存达到120M时直接强制关闭程序.所以出现闪退除了是程序出现逻辑错误,还有可能是内存使用过大. (1)创建一个对象的过程:先分配内存空间存储对象:初始化成员变量:返回对象的指针. (2)对象在创建时,内部会自动创建一个引用计数器retainCount,当retainCount=0时,系统会回收当前对象,retainCount是唯一判断标记.release会-1,retain会+1,retain后返回的是自己se

block没那么难(三):block和对象的内存管理

本系列博文总结自<Pro Multithreading and Memory Management for iOS and OS X with ARC> 在上一篇文章中,我们讲了很多关于 block 和基础变量的内存管理,接着我们聊聊 block 和对象的内存管理,如 block 经常会碰到的循环引用问题等等. 获取对象 照例先来段代码轻松下,瞧瞧 block 是怎么获取外部对象的 /********************** capturing objects **************

iOS核心语言Objective C语言 —— 内存管理

本分享是面向有意向从事iOS开发的伙伴以及苹果产品的发烧友们,或者已经从事了iOS的开发者,想进一步提升者.如果您对iOS开发有极高的兴趣,可以与我一起探讨iOS开发,一起学习,共同进步.如果您是零基础,建议您先翻阅我之前分享的iOS开发分分钟搞定C语言系列,然后在开始Objective C语言的学习,如果您遇到问题也可以与我探讨,另外将无偿分享自己整理出来的大概400G iOS学习视频及学习资料,都是干货哦!可以新浪微博私信?关注极客James,期待与您的共同学习和探讨!!由于时间有限,每天在

PHP笔记 变量和内存管理

鸟哥的博客是个大宝库,正在从头看起. <深入理解PHP内存管理之谁动了我的内存> http://www.laruence.com/2011/03/04/1894.html 1.memory_get_usage 函数 int memory_get_usage ([ bool $real_usage = false ] ) PHP脚本使用的内存(不含 memory_get_usage() 函数本身占用的内存) memory_get_usage()的参数$real_usage,默认为 FALSE:当

小白学开发(iOS)OC_ 单个对象的内存管理(2015-08-02)

// //  main.m //  单个对象的内存管理 // //  Created by admin on 15/8/3. //  Copyright (c) 2015年 admin. All rights reserved. // #import <Foundation/Foundation.h> #import "Person.h" void test(); void test5(Person * p); int main(int argc, const char *

小白学开发(iOS)OC_ set方法的内存管理分析(2015-08-04)

// //  main.m //  set方法的内存管理分析 // //  Created by admin on 15/8/5. //  Copyright (c) 2015年 admin. All rights reserved. // #import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { //p