接上篇 内存管理

    引用计数

每个对象都有一个与之相关联多整数,被称作它的引用计数器或保留计算器,当某段代码需要访问一个对象时,该代码就将该对象的保留计数器值加1,表示我要访问该对象,当这段代码访问结束的时候,将对象的保留计数器数值减1,表示不再访问该对象了。当保留计数器的值为0时,表示不再有代码访问该对象了,此时它将被销毁同时占用的内存被回收。

对象的保留计数器值初始值为1。当一个对象即将要被销毁的时候,OC会向对象发送一条dealloc消息,这条消息可以在自己的对象中重写。

常见的几种调用方法:

-(id) retain;

-(oneway void) release;

-(NSUInteger) retainCount;

Eg:

#import <Foundation/Foundation.h>


@interface RetainTra : NSObject


@end


@implementation RetainTra

-(id) init

{

    if (self = [super init]) {

        NSLog(@"init : This is first call -- %d .",[self retainCount]);

    }

    return (self);

}

-(void) dealloc

{

    NSLog(@"dealloc called . Bye Bye..");

    [super dealloc];

}

@end


int main(int argc, const char * argv[]) {

RetainTra *tracker = [ RetainTra new]; //count:1 此处直接调用了init

    

    [tracker retain];//count 2

    NSLog(@"%d--1",[tracker retainCount]);

    

    [tracker retain];//count 3

    NSLog(@"%d--2",[tracker retainCount]);

    

    [tracker release]; //count 2

    NSLog(@"%d--3",[tracker retainCount]);

    

    [tracker release]; //count 1

    NSLog(@"%d--4",[tracker retainCount]);

    

    [tracker release]; //count 0 调用了dealloc。。

    //NSLog(@"%d--5",[tracker retainCount]);

  

    return 0;


}


对象所有权

    如果一个对象内有指向其他对象的实例变量,则称该对象拥有这些对象。如。car对象拥有其指向的engine和tire对象。同样,如果一个函数创建了一个对象,则称该函数拥有这个对象。

访问方法中的保留和释放

    一个更好的解决对象释放问题的代码

-(void) setEngine :(Engine *) newEngine

{

[newEngine retain];

[engine release];

engine = newEngine;

}//setEngine

首先保留了新的engine对象,即使newEngine与engine是同一个对象,保留计数器的值也将先增加,然后立即减少。由于没有归0,engine对象意外的未被销毁,这样避免了错误发生。

在访问方法中,如果先保留新对象,然后再释放对象就不会出现问题了。

自动释放

    不知道为什么,看到这个词的时候,忽然很开心。。。

  自动释放池(autorelease pool) 代码中为@autoreleasepool或NSAutoreleasePool。

当使用@autoreleasepool{}时,所有花括号里面的代码都会被放入到这个新池子中,如果程序运算是内存密集型的,可以使用这种自动释放池。

另外任何在花括号中定义的变量在括号外就无法使用了,这就像典型的C语言中的有效范围,例如循环代码。

NSAutoReleasePool *pool;

pool = [NSAutoreleasePool new];

[pool release];

推荐使用关键字方法,因为关键字方法处理速度大于直接使用对象方法的速度。

自动释放池工作流程

#import <Foundation/Foundation.h>


@interface RetainTra : NSObject

@end


@implementation RetainTra

-(id) init

{

    if (self = [super init]) {

        NSLog(@"init : Retain count of %lu .",[self retainCount]);

    }

    return self;

}

-(void) dealloc

{

    NSLog(@"dealloc called . ByeBye . . . ");

    [super dealloc];

}


@end

int main(int argc, const char * argv[]) {


    NSAutoreleasePool *pool;

    pool = [[NSAutoreleasePool allocinit];

    

    RetainTra *tracker;

    tracker = [RetainTra new]; //count == 1

    NSLog(@"Releaseing pool========1tracker============== %lu",[tracker retainCount]);

    [tracker retain]; //count == 2

    NSLog(@"Releaseing pool=========2tracker============= %lu",[tracker retainCount]);

    [tracker autorelease];// count == 2 still

    NSLog(@"Releaseing pool==========3tracker============ %lu",[tracker retainCount]);

    [tracker release];// count == 1

    NSLog(@"Releaseing pool===========4tracker=========== %lu",[tracker retainCount]);

    [pool release];//release

    NSLog(@"Releaseing pool===========5tracker=========== %lu",[tracker retainCount]);

    

    @autoreleasepool {

        RetainTra *tracker2;

        tracker2 = [RetainTra new]; // count == 1

        NSLog(@"Releaseing pool============6tracker2========== %lu",[tracker2 retainCount]);

        [tracker2 retain]; //count == 2

        NSLog(@"Releaseing pool============7tracker2========== %lu",[tracker2 retainCount]);

        [tracker2 autorelease];// count == 2

        NSLog(@"Releaseing pool=============8tracker2========= %lu",[tracker2 retainCount]);

        [tracker2 release];// count == 1

        

        NSLog(@"auto releasing pool");

    }

    return 0;

    


}

通过设置断点,在lldb中可以清楚的看到每一步的执行顺序,另外,释放自动释放池之前的NSLog()函数先于RetainRra类中的NSLog()函数被调用。

Cocoa内存管理规则

1.当使用new,alloc,copy方法创建一个对象时,该对象的保留计数器的值为1,当不再使用该对象时,应该向该对象发送一条release或者autorelease消息来确保该对象在寿命结束时被销毁。

2.当通过其他方法获得一个对象时,假设该对象保留计数器的值为1,且已被设置为自动释放,那么你不需要执行任何操作来确保该对象得到清理。如果你打算在一段时间内拥有该对象,则需要保留它并确保在操作完成时释放它。

3.如果你保留了某个对象,就需要最终释放或者自动释放该对象,必须保持retain方法和release方法使用的次数相等。

- “如果为使用了new,alloc,copy方法获得了一个对象就释放或自动释放该对象。”


获得途径 临时对象拥有对象
alloc/new/copy不再使用时释放对象在dealloc中释放对象
其他方法不需要执行任何操作获得对象时保留,在dealloc中释放对象

垃圾回收

垃圾回收器和自动释放池一样,也是在事件循环结束时触发的。垃圾回收功能只支持OS X应用开发,不支持iOS。

自动引用计数ARC(atuomatic reference counting)

ARC是在编译时进行工作的,它会在代码中插入合适的release和retain语句。

说明:ARC只对可保留的对象指针(ROPs)有效,可保留的对象指针主要有三种:

1.代码块指针。 2.Objective-C对象指针。3.通过__attribute__(NSObject)类型定义的指针。

使用ARC的三个条件

1.对象的最上层集合知道如何去管理它的子对象。例如通过malloc方法创建一个字符串数组:NSString **myString; myString = malloc(10 * sizeof(NSString *));  这两行代码创建了一个指向10个字符串的C型数组,而C型数组为不可保留对象,所以这里无法在这个结构体中使用ARC特性。

2.你必须能够对某个对象的保留计数器的值进行加1或减1的操作,也就是说所有的NSObject类的子类都可以进行内存管理。

3.在传递对象的时候,你的程序需要能够在调用者和接收者之间传递所有权。

如果你管理了某个对象那么就拥有对这个对象的强引用,如果没有管理,那么你拥有的则是弱引用,比方说:对属性使用了assign特性,你便创建了一个弱引用。弱引用有利于处理保留循环。使用弱引用的时候,该对象的保留计数器的值不会增加。

当出现如图所示情况的时候,若A先释放了对象B,那么C仍然拥有指向B的弱引用,但因为引用失效了,会直接导致出现问题,因为B那里已经没有有效值了。这种情况应该让对象自己去清空弱引用对象,这种特殊的弱引用被称为归零弱引用。因为在指向的对象释放之后,这些弱引用就会被设置成零(即nil),就可以像平常的指向nil的指针一样被处理掉。

A (强引用)B(弱引用) C

Objective-C 内存管理(2) _Objective-C 内存管理

使用归零弱引用必须声明,两种方式:1.声明变量时使用__weak关键字。2.对属性使用weak特性。

__weak NSString *myString; @property(weak) NSString *myString;

使用ARC时的两种命名规则:

1.属性名不能以new开头,如@property NSString *newString是不允许的。2.属性不能只有一饿read-only而没有内存管理特性。若没启用ARC,可以使用@property(readonly)  NSString *title语句,但是如果启用了ARC,就必须指定由谁来管理内存。

拥有者权限

为了让ARC便于工作,需要告诉编译器哪个对象是指针的所有者。可以使用一种叫做 桥接转换的C语言技术。三种方式:

1. __bridge操作符:会传递指针但不会传递它的所有权。例如: cfString = (__bridge CFStringRef)theString; cfString接收了指令,但指针但所有权仍由theString保留。

2.__bridge_retained操作符:使用这种操作符,所有权会转移到non-ROP上。例如:cfString = (__bridge_retained CFStringRef)theString;

3._bridge_transfer操作符:它把所有权转交给ROP,在这个示例中ARC拥有对象并能确保它会像其他ARC对象一样得到释放。

异常:

异常的所有关键字都是以@开头的。@try 定义用来测试的代码块以决定是否要抛出异常。@catch() 用来处理已抛出异常的代码块,接收一个参数,通常是NSException类型。@finally 定义无论是否抛出一超女都会执行代码块。@throw 抛出异常。

结构: @try{//要捕捉的代码,可能会出错的代码} @catch (NSException *exception){// 代码执行处理异常。} @finally {//总是会被执行的代码。通常为清理}

使用goto和return可以退出异常处理代码。

创建一个异常:NSException *theException = [ NSException exceptionWithName: …];

抛出异常使用:@throw theException; 或[theException raise];两者区别是raise只对NSException对象有效,而@throw也可以用在其他对象上。

异常也需要内存管理。

本章总结:

内存管理太难理解了,相比以前所学的内容来说``


每个对象都维护一个保留计数器。其值为1,当值为0时,对象被销毁,在销毁对象时,首先调用dealloc方法,然后回收其内存。

当对象接受一条autorelease消息时,其计数器值不会改变,只是该对象被放入来自动释放池中,当释放池被销毁时,会向池中所有对象发送release消息。

如果保留了某对象,则必须保持retain和release方法的使用次数相等。