zoukankan      html  css  js  c++  java
  • iOS之内存管理(ARC)

    每天学习一点点 编程PDF电子书免费下载: http://www.shitanlife.com/code

    iOS的内存管理,相信大家都不陌生,之前是使用的MRC,由开发人员手动来管理内存,后来使用了ARC,来由系统管理内存。本文主要讲讲Autorelease,Core Foundation对象在内存管理方面要注意的地方。

    Autorelease

    提到内存管理,就不得不提autorelease,虽然我们平时开发中很少会感知到它的存在。autorelease就是自动释放的意思,如果变量使用autorelease来修饰,就表明变量的释放由系统来完成。

    autoreleasepool是由runloop在开启或者唤醒的时候创建的,当runloop进入睡眠或者释放掉的时候,autoreleasepool会给pool中的所有对象发送release消息。那么,由此便引申出一个问题,如果runloop不进入睡眠或者不释放(例如:主线程,或者某些常驻线程),pool里面的对象也便不会被释放,他们会堆积在内存中,但是系统会做一些优化,如下:

    1
    2
    3
    4
    5
    6
    7
    8
    - (NSMutableArray*)createArrayNoAutorelease{
        id arr = [NSMutableArray arrayWithCapacity:3];
        return arr;
    }
     
    - (NSMutableArray*)createArrayAutorelease{
        return [NSMutableArray arrayWithCapacity:3];
    }

    上面的两个方法都是表明的要返回NSMutableArray这个对象,但是两种写法不同,系统做的处理也不相同。

    我们先调用第一个方法 createArrayNoAutorelease方法,然后使用xcode的Product->Perform Action->Assemble xxx来看看,生成如下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    Lfunc_begin1:
    ....前面省略
        bl  _objc_retainAutoreleasedReturnValue
        .loc    2 56 14                
        str r0, [sp, #4]
        .loc    2 57 12 is_stmt 1      
        ldr r0, [sp, #4]
        bl  _objc_retain
        add r1, sp, #4
        movs    r2, #0
        .loc    2 58 1                 
        str r0, [sp]                @ 4-byte Spill
        mov r0, r1
        mov r1, r2
        bl  _objc_storeStrong
        ldr r0, [sp]                @ 4-byte Reload
        .loc    2 58 1 is_stmt 0       
        add sp, #16
    Ltmp3:
        pop.w   {r7, lr}
        b.w _objc_autoreleaseReturnValue
    Ltmp4:
    Lfunc_end1:

    其中 objc_retainAutoreleasedReturnValue和objc_autoreleaseReturnValue主要用于优化程序运行。本来应该将返回的对象注册到autoreleasepool中,但是有了这两个函数,就可以不将对象注册到autoreleasepool中,而是直接传递给调用方,这是性能调优的一个举措。

    我们再来看看调用createArrayAutorelease方法,如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    Lfunc_begin0:
    ... 省略
        bl  _objc_retainAutoreleasedReturnValue
        add r1, sp, #4
        movs    r2, #0
        .loc    2 44 8                
        str r0, [sp, #4]
        .loc    2 49 1 is_stmt 1       
        mov r0, r1
        mov r1, r2
        bl  _objc_storeStrong
        add sp, #24
        pop {r7, pc}
    Ltmp1:
    Lfunc_end0:

    现在对象被注册到了autoreleasepool中。我们可以使用:

    1
    po [NSAutoreleasePool showPools]

    来看看当前autoreleasepool的状况,会发现多出了一个对象,如下图:

    (我只截取了一部分)

    注意:对于alloc/new/copy/mutableCopy这样的方法作为返回对象,编译器会将他们优化为createArrayNoAutorelease相同的情况

    关于autoreleasepool的内部结构,实现原理等,可以参看:

    http://www.cocoachina.com/ios/20150610/12093.html

    Core Foundation

    Core Foundation对象是一组由c语言接口,可以跟Foundation框架的OC对象相互转换。

    要搞清楚Core Foundation对象的内存管理,就需要搞清楚:__bridge, __bridge_retained, __bridge_transfer; CFRetain(), CFRelease() 这几个关键词的概念。

    CFRetain(), CFRelease() :Core Foundation对象的内存管理方式,跟之前MRC时代的retain和release很像。

        {
            CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, "test", kCFStringEncodingUTF8);
            NSLog(@"%@", str);
            CFRelease(str);
        }

    注意:这里要调用CFRelease(str)方法,不然会有内存泄漏。

    __bridge:只做类型转换,不修改对象持有者。如下:

    复制代码
        {
            CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, "test", kCFStringEncodingUTF8);
            NSString *obj = (__bridge NSString*)str;
            NSLog(@"%@", obj);
    //        CFRelease(str);
        }
    复制代码

    对于从CF转换为OC对象,一定要调用CFRelease(str)方法,不然会有内存泄漏,因为只是做了简单的指针地址变换,str仍然没有释放。

    来看一个野指针的例子:

    复制代码
        CFMutableArrayRef cfObject = NULL;
        {
            id obj = [[NSMutableArray alloc] init];
            cfObject = (__bridge CFMutableArrayRef)obj;
            CFShow(cfObject);
        }
        CFRelease(cfObject);
    复制代码

    因为__bridge不持有obj对象,所以当大括号结束以后,obj被释放,cfObject就成为了野指针,在调用CFRelease方法时就会引发程序崩溃。

    __bridge_retained:用于将OC对象转换为CF对象,持有者也发生改变,需要调用CFRelease方法。

    __bridge_transfer:将CF对象转换为OC对象,同时将对象持有权交给ARC,不需要调用CFRelease方法,如下:

    复制代码
        {
            CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, "test", kCFStringEncodingUTF8);
            NSString *obj = (__bridge_transfer NSString*)str;
            NSLog(@"%@", obj);
    //        CFRelease(str);
        }
    复制代码

    所以在使用CF对象时,要特别注意内存问题。

    参考文章:

    https://yq.aliyun.com/articles/58964

    https://juejin.im/entry/579bfdfe5bbb500064d18aca

    每天学习一点点 编程PDF电子书免费下载: http://www.shitanlife.com/code

  • 相关阅读:
    Laya中使用Protobuf
    Laya中第三方库的使用
    Laya的骨骼换装
    Laya的粒子效果
    Laya的预设Prefab (预制件)
    Egret EUI Tab + ViewStack
    Egret 划线手势动画 (切水果)
    Egret3.2.6老项目转成5.2.22微信小游戏,遇到exml加载不了问题
    AndroidUI设计 之 图片浏览器
    Android应用的自动更新模块
  • 原文地址:https://www.cnblogs.com/scode2/p/8671214.html
Copyright © 2011-2022 走看看