zoukankan      html  css  js  c++  java
  • Objective-C语言内存管理

    • Objective-C为每个对象提供一个内部计数器,这个计数器跟踪对象的引用次数。所有类都继承自 NSObject 的对象retain和release方法。

    如果使用了new、alloc或copy方法获得一个对象,则我必须释放(release)或自动释放(autorelease)该对象

    复制分为浅层复制(指向同一个地址)和深层复制(创建不同的实例)。

    • 当对象被创建或拷贝时候,引用计数为1。每次保持对象时候,就发送一条retain消息,使其引用计数加1,如果不需要这个对象就是发送一个release消息使其引用计数减1。当对象的引用计数为0的时候,系统就知道不再需要这个对象了,就会释放它内存。

    • 一个对象的创建可以通过alloc分配内存或copy复制(深层复制),这关系到的方法有:alloc, allocWithZone:, copy,copyWithZone:, mutableCopy(可变对象),mutableCopyWithZone:,这些方法都可以使引用计数为1,retain会使引用计数加1,release会使引用计数减1。

    当对象包含其它对象时,就得在 dealloc中自己释放它们。

    @interface Song : NSObject {
    NSString *title;
    NSString *artist;
    long int duration;
    }
    //操作方法
    - (void)start;
    - (void)stop;
    - (void)seek:(long int)time;
    - //访问成员变量方法
    @property NSString *title;
    @property NSString *artist;
    @property(readwrite) long int duration;
    //构造方法
    -(Song*) initWithTitle: (NSString *) title andArtist:
    (NSString *) artist
    andDuration:( long int )duration ;
    @end

     重写dealloc

    
    #import "Song.h"
    
    @implementation Song
    
    @synthesize title;
    @synthesize artist;
    @synthesize duration;
    
    //构造函数
    -(Song*) initWithTitle: (NSString *) newTitle andArtist: (NSString *) newArtist andDuration:(long int)newDuration {
        self = [super init];
        if ( self ) {
            self.title = newTitle;
            self.artist = newArtist;
            self.duration = newDuration;
        }
        return self;
        
    }
    
    - (void)start {
        //开始播放
    }
    
    - (void)stop {
        //停止播放
    }
    
    - (void)seek:(long int)time {
        //跳过时间
    }
    
    -(void) dealloc {
        NSLog(@"释放Song对象...");
        [title release];
        [artist release];
        [super dealloc];
    }
    
    @end
    #import <Foundation/Foundation.h>
    #import "Song.h"
    int main (int argc, const char * argv[]) {
    Song *song1 = [[Song alloc] initWithTitle:@"Big Big World"
    andArtist:@"奥斯卡.艾美莉亚" andDuration:180];
    Song *song2 = [[Song alloc] initWithTitle:@"It's ok"
    andArtist:@"atomic kitten" andDuration:280];
    // print current counts
    NSLog(@"song 1 retain count: %i", [song1 retainCount] );
    NSLog(@"song 2 retain count: %i", [song2 retainCount] );
    // increment them
    [song1 retain]; // 2
    [song1 retain]; // 3
    [song2 retain]; // 2
    // print current counts
    NSLog(@"song 1 retain count: %i", [song1 retainCount] );
    NSLog(@"song 2 retain count: %i", [song2 retainCount] );
    ... ...
    // decrement
    [song1 release]; // 2
    [song2 release]; // 1
    // print current counts
    NSLog(@"song 1 retain count: %i", [song1 retainCount] );
    NSLog(@"song 2 retain count: %i", [song2 retainCount] );
    // release them until they dealloc themselves
    [song1 release]; // 1
    [song1 release]; // 0
    [song2 release]; // 0
    return 0;
    }

    • 在这个main函数中,声明了两个Song对象,当retain调用增加引用计数,而release调用减少它。调用[objretainCount] 来取得引用计数的 int 值。 当retainCount到达 0,两个对象都会调用dealloc,所以可以看到印出了两个 “释放Song对象...”。在Song对象释放的时候,先要释放它自己的对象类型成员变量title和artist,然后再调用[super dealloc]

    内存释放池(Autorelease pool)提供了一个对象容器(通过类方法创建的对象不能release,而要通过pool释放),每次对象发送autorelease消息时,对象的引用计数并不真正变化,而是向内存释放池中添加一条记录,记下对象的这种要求,直到当内存释放池发送drain或release消息时,当池被销毁前会通知池中的所有对象,全部发送release消息真正将引用计数减少。

    垃圾收集混合环境下:应该使用drain方法,因为release在GC模式下没有意义

    • 这些语句必须要放在下面语句之间,直到池被释放,一个对象要想纳入内存释放池对象,必须要发送autorelease

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    … …
    [pool release];// [pool drain];
    #import <Foundation/Foundation.h>
    int main (int argc, const char * argv[]) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSArray *weeksNames1 = [NSArray arrayWithObjects:
    @"星期一",@"星期二",@"星期三",@"星期四"
    ,@"星期五",@"星期六",@"星期日",nil];
    NSArray *weeksNames2 = [[NSArray alloc] initWithObjects:
    @"星期一",@"星期二",@"星期三",@"星期四"
    ,@"星期五",@"星期六",@"星期日",nil];
    //[weeksNames1 release];
    //[weeksNames1 autorelease];
    //[weeksNames2 release];
    //[weeksNames2 autorelease];
    NSLog(@" retain count: %i" , [weeksNames1 retainCount] );
    NSLog(@" retain count: %i" , [weeksNames2 retainCount] );
    [pool release];
    return 0;
    }

    NSArray类是Foundation框架提供的不可变数组类,Foundation框架中对象的创建有两类方法:类方法(+)构造方法和实例方法(-)构造方法。打开NSArray ClassReference文档,其中创建对象有关的方法如图所示。

    从NSArray Class Reference文档可以看出,以+和类名开头(去掉NS,小写第1个字母,array),就是类级构造方法,以-和initWith开头的就是实例构造方法。类级构造方法不能使用release,可以不用autorelease就可以自动纳入内存释放池管理。实例构造方法,如果发出release消息就马上释放对象,如果发出autorelease消息可以自动纳入内存释放池管理,不会马上释放。在iOS开发中由于内存相对少,因此基本上都采用实例构造方法实例化对象,采用发送release消息立刻释放对象内存。

    有的时候我们声明的对象要跨越对象调用,内存管理就会变的更加复杂。例如在Song类中有一对成员变量存取的方法,当然可以把它们封装成属性,通过属性参数来管理内存。在Song类中给成员变量设置方法如下:

    - (void)setTitle:(NSString *)newTitle {
    title = newTitle;
    }
    - (void)setArtist:(NSString *)newArtist {
    artist = newArtist;
    }

    这段代码事实上有内存泄漏,当设置一个新的Title时候,title = newTitle只是将指针改变了(把新对象的指针赋给了旧对象), 旧对象并没有释放,所以我们会这样修改这些方法:

    - (void)setTitle:(NSString *)newTitle {
    [newTitle retain];//保持,防止释放
    [title release];//把旧的释放
    title = [[NSString alloc] initWithString: newTitle];//给新对象重新分配内存创建对象
    [newTitle release];//释放传递进来的对象
    }
    - (void)setArtist:(NSString *)newArtist {
    [newArtist retain];
    [artist release];
    artist = [[NSString alloc] initWithString: newArtist];
    [newArtist release];
     }

    首先保留新对象,释放旧对象,然后使用实例构造方法实例化新的对象。参数newTitle不要在方法中释放。由于基本数据类型(非对象类型)不需要释放,因此下面的写法是没有问题的。

    - (void)setDuration:(long int)newDuration {
    duration = newDuration;
    }

    此外,在构造方法中也必须要注意,不能直接赋值title =newTitle,而是要调用自身的设置方法

    //构造方法
    -(Song*) initWithTitle: (NSString *) newTitle andArtist:
    (NSString *) newArtist
    andDuration:(long int)newDuration {
    self = [super init];
    if ( self ) {
    [self setTitle:newTitle];
    [self setArtist:newArtist];
    [self setDuration:newDuration];
    }
    return self;
    }

    assign参数代表设置时候直接赋值(用于基本数据类型),而不是复制或者保留它。这种机制非常适合一些基本类型,比如NSInteger和CGFloat,或者就是不想直接拥有的类型,比如委托。assign相当于如下写法。

    - (void)setTitle:(NSString *)newTitle {
    title = newTitle;
    }

    retain参数会在赋值时把新值保留(发送retain消息)。此属性只能用于Objective-C对象类型,而不能用于基本数据类型或者Core Foundation。retain相当于如下写法:

    (void)setTitle:(NSString *)newTitle {
    [newTitle retain];
    [title release];
    title = [[NSString alloc] initWithString: newTitle];
    [newTitle release] }

    copy在赋值时将新值拷贝一份,拷贝工作由copy方法执行,此属性只对那些实行了NSCopying协议的对象类型有效。copy相当于如下写法:

    - (void)setTitle:(NSString *)newTitle {
    [newTitle copy];
    [title release];
    title = [[NSString alloc] initWithString: newTitle];
    [newTitle release] }
  • 相关阅读:
    Adobe Flash Builder 4.5 Android Air 程序开发系列 之六 多点触控
    Adobe Flash Builder 4.5 Android Air 程序开发系列 之九 定位
    Adobe Flash Builder 4.5 Android Air 程序开发系列 之七 重力感应
    Adobe Flash Builder 4.5 Android Air 程序开发系列 之五 保存数据的几种方式
    Adobe Flash Builder 4.5 Android Air 程序开发系列 之八 照相机
    Adobe Flash Builder 4.5 Android Air 程序开发系列 之三 Application 配置详解
    Adobe Flash Builder 4.5 Android Air 程序开发系列 之四 打开与关闭应用程序是的保存数据
    ADOBE FLASH BUILDER 4.6 IOS 开发之部署与调试
    [译] 高性能JavaScript 1至5章总结
    页签及盒子的web标准实现
  • 原文地址:https://www.cnblogs.com/ecollab/p/6124438.html
Copyright © 2011-2022 走看看