zoukankan      html  css  js  c++  java
  • iphone内存管理的一些整理

    内存管理个人总结

    无论编写任何程序,都需要确保能够有效和高效地管理资源。程序内存就是这些资源中的一种。在Objective-C程序中,必须确保所创建的对象,在不再需要它们的时候被销毁。

    注意:iOS不提供垃圾回收机制。

    一般规则:

    只能释放或自动释放自己所拥有的对象

    一般以alloc ,new, copy创建的对象都具有所有权,或者如果向一个对象发送了一条retain消息,也会获得该对象的所有权。

    此时需要用release进行释放或者调用自动释放池autorelease;自动释放池的调用是系统来完成的。

    需要将接收到的对象存储为某个实例变量的属性,您必须保留或复制该对象。

    释放实例:

    UIView *view 1= [[UIView alloc]init];

    Self.view = view1;

    [view1 release];

    不需要释放的例子:

    UIButton*button=[UIButton buttonWithType:UIButtonTypeRoundedRect];

    由于调用的类方法没有使用alloc,new,copy等所以它自动释放,不需要手动释放。

    调用自动释放池的例子:

    -(NSArray *)rearray

    {

    NSArray *array = [[NSArray alloc]initWithObjects:@”1”,@”2”,nil];

    return [array autorelease];

    }

    这个方法既没有破坏内存规则,又避免了接收值的使用者不知道何时销毁对象的现象。如果直接return array会造成内存泄露,如果先释放在返回的话会返回无效对象([array release];return array;)这也是错误的。当然如果直接调用类方法就可以这样做:

    -(NSArray *)rearray

    {

    NSArray *array = [NSArray arrayWithObjects:@”1”,@”2”,nil];

    return array;

    }

    在 Objective-C 程序中,对象会被创建和销毁。为了确保应用程序不会使用不必要的内存,对象应该在不需要它们的时候被销毁。当然,在需要对象时保证它们不被销毁也很重要。

    任何对象都可能拥有一个或多个所有者。只要一个对象至少还拥有一个所有者,它就会继续存在。如果一个对象没有所有者,则运行时系统会自动销毁它。

    可以使用retain来获得一个对象的所有权。

    保留计数(retainCount)

    在调用retain方法后通过引用计数—通常被称为“保留计数”—实现的。每个对象都有一个保留计数。

    创建一个对象时,该对象的保留计数为1。

    向一个对象发送retain消息时,该对象的保留计数加1。

    向一个对象发送release消息时,该对象的保留计数减1。

    向一个对象发送autorelease消息时,该对象的保留计数会在将来的某个阶段减1。

    如果一个对象的保留计数被减为0,该对象就会被回收.(直接调用dealloc进行释放)。

    当显式地查询对象的保留计数是多少。由于添加了保护机制,当保留计数被减为0时,控制台输出仍然为1因此会造成误导。

    共享对象的有效性

    Cocoa的所有权策略规定,被接收的对象通常应该在整个调用方法的作用域内保持有效。此外,还可以返回从当前作用域接收到的对象,而不必担心它被释放。对象的getter方法返回一个缓存的实例变量或者一个计算值,这对您的应用程序来说无关紧要。重要的是,对象会在您需要它的这段期间保持有效。

    这一规则偶尔也有一些例外情况,主要可以总结为以下两类。

    当对象从一个基本的集合类中被删除的时候。

    heisenObject = [array objectAtIndex:n];

    [array removeObjectAtIndex:n];

    // heisenObject could now be invalid.

    当对象从一个基本的集合类中被删除时,它会收到一条release(不是autorelease)消息。如果该集合是这个被删除对象的唯一所有者,则被删除的对象(例子中的heisenObject)将被立即回收。

    当一个“父对象”被回收的时候。

    id parent = <#create a parent object#>;

    // ...

    heisenObject = [parent child] ;

    [parent release]; // Or, for example: self.parent = nil;

    // heisenObject could now be invalid.

    在某些情况下,您通过另外一个对象得到某个对象,然后直接或间接地释放父对象。如果释放父对象会使其被回收,而且父对象是子对象的唯一所有者,那么子对象(例子中的heisenObject)将同时被回收(假设它在父对象的dealloc方法中收到一条release而非autorelease消息)。

    为了防止这些情况发生,您要在接收heisenObject后保留该对象,并

    在使用完该对象后对其进行释放,例如:

    heisenObject = [[array objectAtIndex:n] retain];

    [array removeObjectAtIndex:n];

    // use heisenObject.

    [heisenObject release];

    如果在您的类中有实例变量对象,您必须实现一个dealloc方法来释放它们,然后调用超类的dealloc实现。

    重要:决不要直接调用另一个对象的dealloc方法。

    保留循环

    在某些情况下,两个对象之间可能会出现循环引用的情况,也就是说,每一个对象都包含一个实例变量引用对方对象。例如,考虑一个文本程序,程序中对象间的关系如图1所示。“文档(Document)”对象为文档中的每个页面创建一个“页(Page)”对象。每个Page对象具有一个实例变量,用来跟踪该页所在的文档。如果Document对象保留了Page对象, 同时Page对象也保留Document对象,则这两个对象都永远不会被释放。只有Page对象被释放,Document的引用计数才能变为0,而只有Document对象被回收,Page对象才能被释放。

    针对保留循环问题的解决方案是“父”对象应保留其“子”对象,但子对象不应该保留其父对象。因此,在图1中,document对象要保留page对象,但page对象不保留document对象。子对象对其父对象的引用是一个弱引用的例子,这部分内容在“对象的弱引用”有更充分的描述。

    保留一个对象创建了一个对该对象的“强”引用。一个对象只有在它的所有强引用都被释放后才能被回收。因此,一个对象的生命周期取决于其强引用的所有者。在某些情况下,这种行为可能并不理想。您可能想要引用一个对象而不妨碍对象本身的回收。对于这种情况,您可以获取一个“弱”引用。弱引用是通过存储一个指向对象的指针创建的,而不是保留对象。

    NSMutableArray *array;

    NSUInteger i;

    // ...

    for (i = 0; i < 10; i++) {

        NSNumber *allocedNumber = [[NSNumber alloc] initWithInteger: i];

        [array addObject:allocedNumber];

        [allocedNumber release];

    }

    在这段代码中,您需要在for循环的作用域内向allocedNumber发送release消息,以抵消之前的alloc。由于数组在用addObject:方法添加数字时对其进行了保留,因此只要它还在数组中就不会被释放。

    要理解这一点,您要把自己放在实现这种集合类的作者的位置。您要确保交给您管理的对象不能在您的眼皮底下消失,所以您要在这些对象被加入集合中时向它们发送retain消息。如果它们被删除,您还必须相应地发送release消息,并且在您自己的dealloc方法中,您还应该向其余的对象发送release消息。

    您可以使用常见的alloc和init消息来创建一个NSAutoreleasePool对象,并使用drain(如果您向一个自动释放池发送autorelease或retain消息,会引发异常—要了解release和drain之间的差异,请参考“垃圾回收”)销毁它。自动释放池应该总是在与它被创建时所处的相同上下文环境(方法或函数的调用,或循环体)中被销毁。

    自动释放池被置于一个堆栈中,虽然它们通常被称为被“嵌套”的。当您创建一个新的自动释放池时,它被添加到堆栈的顶部。当自动释放池被回收时,它们从堆栈中被删除。当一个对象收到送autorelease消息时,它被添加到当前线程的目前处于栈顶的自动释放池中。

    嵌套自动释放池的能力是指,您可以将它们包含进任何函数或方法中。例如,main函数可以创建一个自动释放池,并调用另一个创建了另外一个自动释放池的函数。或者,一个方法可以有一个自动释放池用于外循环,而有另一个自动释放池用于内循环。嵌套自动释放池的能力是一种很显著的优势,但是,当发生异常时也会有副作用

    我们通常会提及自动释放池是被嵌套的,如清单1所示。但是,您也可以认为嵌套自动释放池位于一个堆栈中,其中,“最内层”的自动释放池位于栈顶。如前所述,嵌套自动释放池实际上是这样实现的:程序中的每个线程都维护一个自动释放池的堆栈。当您创建一个自动释放池时,它被压入当前线程的堆栈的栈顶。当一个对象被自动释放时—也就是说,当一个对象收到一条autorelease消息或者当它作为一个参数被传入addObject:类方法时—它总是被放入堆栈顶部的自动释放池中。

    在垃圾回收环境中,release是一个空操作。因此,NSAutoreleasePool提供了drain方法,在引用计数环境中,该方法的作用等同于调用release,但在垃圾回收环境中,它会触发垃圾回收(如果自上次垃圾回收以来分配的内存大于当前的阈值)。因此,在通常情况下,您应该使用drain而不是release来销毁自动释放池。

  • 相关阅读:
    11. Container With Most Water
    9. Palindrome Number
    375. 猜数字大小 II leetcode java
    leetcode 72 编辑距离 JAVA
    73. 矩阵置零 leetcode JAVA
    快速排序 JAVA实现
    63. 不同路径 II leetcode JAVA
    重写(override)与重载(overload)
    62 不同路径 leetcode JAVA
    leetcode 56 合并区间 JAVA
  • 原文地址:https://www.cnblogs.com/superhappy/p/2332930.html
Copyright © 2011-2022 走看看