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] }
  • 相关阅读:
    《那些年啊,那些事——一个程序员的奋斗史》——35
    《那些年啊,那些事——一个程序员的奋斗史》——34
    《那些年啊,那些事——一个程序员的奋斗史》——36
    《那些年啊,那些事——一个程序员的奋斗史》——36
    《那些年啊,那些事——一个程序员的奋斗史》——35
    《那些年啊,那些事——一个程序员的奋斗史》——35
    《那些年啊,那些事——一个程序员的奋斗史》——36
    大内高手—常见内存错误
    [open source]Lrc歌词解析器发布
    大内高手—共享内存与线程局部存储
  • 原文地址:https://www.cnblogs.com/ecollab/p/6124438.html
Copyright © 2011-2022 走看看