Objective-C 日记⑦ 内存管理

  Cocoa内存管理方法:retain、releaseautorelease

  概要:

    每个对象都维护一个保留计数器:对象创建时其保留计数器值为1,对象被保留时计数器+1;对象释放时保留计数器-1;当保留计数器值为0时对象被销毁,在销毁对象时,首先调用对象的dealloc方法,再回收其占用的内存以供其他对象使用

    当对象接受一条autorelease消息时,其保留计数器值不会立即改变,相反该对象只是被放入到NSAutoreleasePool中。当自动释放池被销毁时,会向池中所有对象发送release消息,所有被自动释放的对象都将其保留计数器值-1。如果保留计数器值为0则对象被销毁。

    当使用AppKit时,Objective-C会在适当的时间为你创建和销毁自动释放池。

 

内存管理黄金法则
  1、使用new、alloc、copy方法创建一个对象时,该对象的计数器值为1.当不再使用该对象时需要向该对象发送一条release或autorelease消息
  2、当通过其他方法获得一个对象时,假设该对象的计数器为1且已经被设置为自动释放,则不需要执行任何操作来确保该对象被清理
  3、如果保留了某个对象,最终需要释放或自动释放该对象,必须保持retain方法和release方法的使用次数相等

简单的说:如果你使用了new、alloc或copy方法获得一个对象,则你必须释放或自动释放该对象

一、临时对象

未打算长期拥有该对象:如果你使用new、alloc、copy方法获得一个对象,则需要安排该对象消亡。通常使用release消息来实现
eg:
    NSMutableArray *array;
    array=[[NSMutableArray alloc] init];//count=1
    [array release];//count=0
    
    注意例外:
    
    如果使用其他方法获得一个对象例如arrayWithCapacity:方法则不需要关心如果销毁该对象:
    NSMutableArray *array;
    array=[NSMutableArray arrayWithCapacity:10];//count=1 autoreleased
原因:
    arrayWithCapacity方法不属于alloc、new、copy这3个方法中的一个,因此可以假设该对象被返回时保留计算器值为1且已经被设置为自动释放。
    当自动释放池被销毁时,向array对象发送release消息,该对象保留计算器值归0,占用的内存被回收
    
    还有NSColor类也是
    NSColor *color;
    color=[NSColor blueColor];
    blueColor方法也不属于alloc、new、copy这3方法。解释同上

二、拥有对象

    希望一直拥有该对象,常见方法:在其他对象的实例变量中使用这些对象将它们加入到例如:NSArray或NSDictionary等集合中或作为全局变量使用。

  如果你正在使用new、alloc或copy创建一个对象则该对象保留计数器值为1也一直被保留但一定要在拥有该对象的对象的dealloc方法中释放该对象
    -(void) doStuff
    {
        flonkArray =[NSMutableArray new];//count 1
    }
    -(void) dealloc
    {
        [flonkArray release];//count 0
        [super dealloc];
    }
    
    上面说你是通过new、alloc、copy创建对象,如果不实用这三个获取对象从其他方式获得创建对象,并且需要拥有对象则考虑编写GUI应用程序事件循环情况
eg:使用自动释放对象,上面代码可以修改为:

  

复制代码
-(void) doSuff
    {
        flontArray =[NSMutableArray arrayWithCapacity:10];//count 1 autorelease(为什么会自动销毁原因在上面)
        [flontArray retain];//count 2 autorelease 这里如果不实用retain那么flontArray可能会被销毁。原因就是arrayWithCapacity
    }
    -(void) dealloc
    {
        [flontArray release];//count 1
        [super dealloc];
    }
    
复制代码

  当事件循环结束或自动释放池被销毁时,flontArray将会接收到一个release消息使得计数器值从2减到1,计数器值>0所以对象继续存在。因此需要到dealloc中释放该对象。

 

那在看看这个例子

    int i;
    for(i=0;i<1000000;i++)
    {
        id object=[someArray objectAtIndex:i];
        NSString *desc=[object description];//description 一些方法这里不需要理他
    }

  如果这样写可能会产生100万个description字符串对象,内存占用会持续的增长,这100万个对象是一直存在的直到自动释放池被销毁时才最终释放。

解决方案:

  解决的方法就是在循环中创建一个自己的自动释放池,这样使得每执行1000次就销毁当前自动释放池并创建一个新的自动释放池:

  

复制代码
    NSAutoreleasePool *pool;
    pool =[[NSAutoreleasePool alloc] init];
    int i;
    for(i=0;i<1000000;i++){
        id object=[someArray objectAtIndex:i];
        NSString *desc=[object description];
        if(i%1000==0){
            [pool release];
            pool =[[NSAutoreleasePool alloc] init];
        }
    }
    [pool release]
复制代码

  每执行1000次新的自动释放池就被销毁,同时新的自动释放池创建。另外自动释放池中的description字符串也不会超过1000条。从而使得内存占用不会连续增加;最终使得自动释放池分配和销毁的操作代价变得很小;

  补充说明:

    自动释放池是以栈的形式出现:当你创建一个新的自动释放池时,它将会被添加到栈顶(栈的工作方式先进先出);接收autorelease消息的对象将被放到最顶端的自动释放池中。如果将一个对象放入到自动释放池中再创建一个新的自动释放池并销毁该新建的自动释放池,则这个自动释放池对象仍将存在,因为容纳该对象的自动释放池仍然存在

  

三、垃圾回收

Objective-C 垃圾回收器是一种继承性的垃圾回收器和自动释放池一样垃圾回收器也是在事件循环结束时触发,但要注意:如果开发iPhone软件则不能使用垃圾回收器,实际上在编写iPhone程序时,苹果公司建议不要在自己的代码中使用autorelease方法同时还要避免使用创建自动释放对象的便利函数。

 

  在XCode中启用下垃圾回收功能(项目信息窗口Build选项卡并选择Required[-fobjc-gc-only]选项即可)即可具体没有mac 也没具体操作,那天买个真机试试再来补充……

 

posted @   PEPE YU  阅读(264)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述
点击右上角即可分享
微信分享提示