zoukankan      html  css  js  c++  java
  • 再谈内存管理与ARC运行机制(一)

    内存管理


    内存在Objective-C开发中是一种相对稀缺的资源,拿Iphone4为例,它的内存只有512mb,所以妥善的处理好所创造,所使用的每个对象与变量都将成为一个问题。在ARC出现以前,同大部分基于C的编程语言一样,程序员需要考虑如何高效的管理内存。像在Java或是一些脚本语言中就不需要考虑内存管理的问题。

    生命周期




    Obj-c对象的创建,存活和销毁整个过程为它的声明周期。销毁后就会回收对象占用的内存。那么跟踪对象的使用,以及理解在什么时候释放资源十分重要。

    看到这样一个问题,问为什么不在dealloc中release viewController而是立即释放呢?

    MainViewController *viewController = [[MainViewController alloc] initWithNibName:@"MainView" bundle:nil];
    self.mainViewController = viewController;
    [viewController release];

    其实理解了持有者的作用就能很快回答,指针viewController在这里只是负责一个MainViewController的创建,以及给mainViewController传值的一个作用,之后它的存在并没有意义了,所以立即release掉来消除引用。

    在频繁的增加持有者的时候,可以用retainCount属性来跟踪持有者的个数,当然在一些难以确定的区域,也可以借助autorelease来帮助管理。
    内存管理好比于保安开和锁住大门。当人要进入公司工作时,先要打卡声明来工作了,走的时候也打卡说明下班了,当所有人都走了之后,保安才能锁住大门,提前锁或不锁都会出现一些问题。

    一般来讲,内存管理遵循一些原则:
    1.自己生成的对象,自己持有它(生成并持有的方法一般为:alloc,new,copy,mutableCopy)
    2.不是自己生成的对象,自己也能持有
    3.不再需要自己持有的对象,立即释放
    4.不能释放不是自己持有的对象

    内部实现


    因为苹果公司不开源的原因,很多研究只能去看GNUstep来说明,GNUstep是Cocoa框架的互换框架。虽然不是完全相同,但从实现来说,应该是十分相近的。

    + alloc方法

    alloc方法调用 +allocWithZone:

    + (id)allocWithZone:(NSZone *)z
    {
    return NSAllocateObject(self, 0, z); //这个方法负责分配内存空间并返回作为对象使用的id类型。
    }

    那么引用计数呢?

    引用计数其实也是保存在一个结构体
    struct obj_layout {
    NSUInteger retained;
    };
    alloc类方法用这个结构体来保存引用计数并将其保存在头部,每调用一次alloc,指向结构体的指针给retained + 1。
    那么访问retained的方法自然就去访问对象指针的头部
    return ((struct obj_layout *) anObject)[-1].retained + 1; //anObject为传入的参数,理解为调用者本身(self)

    那么,retain方法就会使上面的retained值自增1,release方法减1。

    dealloc时,
    struct obj_layout *o = &((struct obj_layout *) anObject)[-1];
    free(o);
    来废弃该结构体。

    在《Objective-c 高级编程》中,作者给出了对苹果实现的猜测。大体思想还是类似的,不过存储方式貌似发生了改变,上面是将引用计数存储在了对象内存块的头部,而苹果实现,将其保存在了引用计数表的记录中。并且各自的好处如下:

    头部管理:
    少量代码即可完成。
    能够统一管理引用计数用的内存块与对象用的内存块。

    引用计数表管理:
    对象用内存块的分配无需考虑内存块头部。
    引用计数表各记录中存有内存块地址,可从各个记录追溯到各对象的内存块。


    autorelease


    经过autorelease的对象,不需要由我们手动去管理内存,而是由自动释放池(Autoreleasepool)来进行管理。尽管如此,只要不废弃AutoreleasePool对象,那么生成的对象就不能被释放,有时可能会产生内存不足的现象。所以要注意释放池的使用。

    来看一下GNUstep对autorelease的实现
    - (id)autorelease
    {
    [NSAutoreleasePool addObject:self];
    }
    在NSAutoreleasePool中对addObject:方法的实现
    - (void)addObject:(id)anObj
    {
    [array addObject:anObj];
    }

    再来看下其他方法
    - (void)drain
    {
    [self dealloc];
    }

    - (void)dealloc
    {
    [self emptyPool];
    [array release];
    }

    - (void)emptyPool
    {
    for (id obj in array) {
    [obj release];
    }
    }

    那么,如果autorelease NSAutorelease对象会怎么样呢?
    答案是肯定的,发生异常:不能自动释放一个自动释放池。

    当然,自动释放池可以嵌套使用
    @autorelease {
    @autorelease {
     
    code...
     
    }
    }

    以上是本篇文章的全部内容,欢迎指正和讨论。转载注明出处~
  • 相关阅读:
    git 查看远程分支、本地分支、删除本地分支
    iOS edgesForExtendedLayout、extendedLayoutIncludesOpaqueBars、automaticallyAdjustsScrollViewInsets属性详解
    【iOS开发】UIWebView与JavaScript(JS) 回调交互
    iOS打印Debug日志的方式
    iOS项目上传到AppStore步骤流程
    IOS开发之实现App消息推送(最新)
    DKNightVersion 的实现 --- 如何为 iOS 应用添加夜间模式
    用Session实现验证码
    HTTP中Get与Post、ViewState 原理
    ASP.NET获取服务器文件的物理路径
  • 原文地址:https://www.cnblogs.com/pangblog/p/3255886.html
Copyright © 2011-2022 走看看