ACCESSobjective-C 的内存管理的-引用计数

obj-c本质就是是”改进了的c语言”,大家都了解c语言是无污染源回收(GC)机制的(注:虽然obj-c2.0后来充实了GC功能,但是当iphone上未可知就此,因此对此iOS平台的程序员来讲,这个几乎从未啥用),所以当obj-c中描绘序时,对于资源的放走得由开发人员手动处理,相对而累一些。

引用计数

就是一样种古老而中之内存管理章程。每个对象(特指:类的实例)内部都来一个retainCount的援计数,对象刚被创造时,retainCount也1,可以手动调用retain方法要retainCount+1,同样为可以手动调用release方法而retainCount-1,调用release方法时,如果retainCount值减到0,系统以自行调用对象的dealloc方法(类似于c#被之dispose方法),开发人员可以以dealloc中释放或理清资源。

1、基本用法

以演示这种基本措施,先定义一个类Sample

恍如接口部分Sample.h

//
//  Sample.h
//  MemoryManage_1
//
//  Created by jimmy.yang on 11-2-19.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import <Foundation/Foundation.h>


@interface Sample : NSObject {

}

@end

恍如实现部分Sample.m

//
//  Sample.m
//  MemoryManage_1
//
//  Created by jimmy.yang on 11-2-19.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "Sample.h"


@implementation Sample

-(id) init
{
    if (self=[super init]){
        NSLog(@"构造函数被调用了!当前引用计数:%d",[self retainCount]);
    }
    return (self);
}

-(void) dealloc{
    NSLog(@"析构函数将要执行...,当前引用计数:%d",[self retainCount]);
    [super dealloc];
}
@end

代码很粗略,除了”构造函数”跟”析构函数”之外,没有其他其它多余处理。

主程序调用

#import <Foundation/Foundation.h>
#import "Sample.h"

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

    Sample *_sample = [Sample new]; //构造函数被调用了!当前引用计数:1
    NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//1  

    [_sample retain];
    NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//2

    [_sample retain];
    NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//3  

    [_sample release];
    NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//2

    [_sample release];
    NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//1

    [_sample release];//析构函数将要执行...,当前引用计数:1
    NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//1,注:即便是在析构函数执行后,如果立即再次引用对象的retainCount,仍然返回1,但以后不管再试图引用该对象的任何属性或方法,都将报错
    NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//对象被释放之后,如果再尝试引用该对象的任何其它方法,则报错
    //[_sample retain];//同上,会报错   

    return 0;   
}

立刻段代码主要说明:对象刚创立时retainCount是否也1,以及retain和release是否可更改retainCount的价值,同时retainCount减至0时,是否会见活动执行dealloc函数

nil 的问题:

1.1
如果光宣称一个Sample类型的变量(其实就算是一个指针),而休实例化,其初始值为nil

1.2
变量实例化以后,就算release掉,dealloc被成功调用,其retainCount并无及时回到0(还能够这调用一蹩脚还仅一蹩脚[xxx
retainCount]),而且指针变量本身吗不见面活动归为nil值

1.3
dealloc被调用后,必须手动赋值nil,retainCount才会自动归0

如上结论是实在试验得下的,见底的代码:

  Sample *s ; 
    NSLog(@"s %@,retainCount=%d",s==nil?@"is nil":@"is not nil",[s retainCount]);//s is nil,retainCount=0   
    s = [Sample new];
    NSLog(@"s %@,retainCount=%d",s==nil?@"is nil":@"is not nil",[s retainCount]);//s is not nil,retainCount=1   
    [s release];
    NSLog(@"s %@,retainCount=%d",s==nil?@"is nil":@"is not nil",[s retainCount]);//s is not nil,retainCount=1
    //NSLog(@"s %@,retainCount=%d",s==nil?@"is nil":@"is not nil",[s retainCount]);//报错:Program received signal:  “EXC_BAD_ACCESS”.
    s = nil;
    NSLog(@"s %@,retainCount=%d",s==nil?@"is nil":@"is not nil",[s retainCount]);//s is nil,retainCount=0

故此绝对别用if (x == nil) 或 if ([x
retainCount]==0)来判定目标是否让销毁,除非你每次销毁对象后,手动显式将该赋值为nil

2、复杂情况

方的演示过于简章,只出一个类自己独耍,如果来差不多只类似,且彼此有联系时,情况只要复杂一些。下面我们筹二单类Shoe和Man(即“鞋子类”和”人“),每个人犹设穿越鞋,所以Man与Shoe之间应是Man拥有Shoe的涉及。

Shoe.h接口定义部分

#import <Foundation/Foundation.h>


@interface Shoe : NSObject {
    NSString* _shoeColor;
    int _shoeSize;
}

//鞋子尺寸
-(void) setSize:(int) size;
-(int) Size;

//鞋子颜色
-(void) setColor:(NSString*) color;
-(NSString*) Color;

//设置鞋子的颜色和尺码
-(void) setColorAndSize:(NSString*) pColor shoeSize:(int) pSize;

@end

Shoe.m实现有

//
//  Shoe.m
//  MemoryManage_1
//
//  Created by jimmy.yang on 11-2-19.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "Shoe.h"


@implementation Shoe

//构造函数
-(id)init
{
    if (self=[super init]){
        _shoeColor = @"black";
        _shoeSize = 35;
    }
    NSLog(@"一双 %@ %d码 的鞋子造好了!",_shoeColor,_shoeSize);
    return (self);
}

-(void) setColor:(NSString *) newColor
{
    _shoeColor = newColor;
}

-(NSString*) Color
{
    return _shoeColor;
}

-(void) setSize:(int) newSize
{
    _shoeSize = newSize;
}   

-(int) Size
{
    return _shoeSize;
}

-(void) setColorAndSize:(NSString *)color shoeSize:(int)size
{
    [self setColor:color];
    [self setSize:size];
}


//析构函数
-(void) dealloc
{
    NSLog(@"%@ %d码的鞋子正在被人道毁灭!",_shoeColor,_shoeSize);
    [super dealloc];
}


@end

Man.h定义有

//
//  Man.h
//  MemoryManage_1
//
//  Created by jimmy.yang on 11-2-20.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Shoe.h"


@interface Man : NSObject {
    NSString *_name;
    Shoe *_shoe;
}


-(void) setName:(NSString*) name;
-(NSString*)Name;

-(void) wearShoe:(Shoe*) shoe;
@end

Man.m实现部分

//
//  Man.m
//  MemoryManage_1
//
//  Created by jimmy.yang on 11-2-20.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "Man.h"


@implementation Man

//构造函数
-(id)init
{
    if (self=[super init]){
        _name = @"no name";
    }
    NSLog(@"新人\"%@\"出生了!",_name);
    return (self);
}


-(void) setName:(NSString *)newName
{
    _name =newName;
}

-(NSString*)Name
{
    return _name;
}

-(void) wearShoe:(Shoe *)shoe
{
    _shoe = shoe;
}

//析构函数
-(void) dealloc
{
    NSLog(@"\"%@\"要死了! ",_name);
    [super dealloc];
}

@end

main函数调用

#import <Foundation/Foundation.h>
#import "Shoe.h"
#import "Man.h"

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

    Man *jimmy = [Man new];
    [jimmy setName:@"Jimmy"];


    Shoe *black40 =[Shoe new];
    [black40 setColorAndSize:@"Black" shoeSize:40];

    [jimmy wearShoe:black40];

    [jimmy release];
    [black40 release];

    return 0;

}

2011-02-23 13:05:50.550
MemoryManage[253:a0f] 新人”no name”出生了!
2011-02-23 13:05:50.560
MemoryManage[253:a0f] 一双双 black 35码 的鞋子造好了!
2011-02-23 13:05:50.634
MemoryManage[253:a0f] “Jimmy”要死了!
2011-02-23 13:05:50.636
MemoryManage[253:a0f] Black 40码的鞋正在为人道毁灭!

以上是出口结果,一切正常,jimmy与black40占据的资源最终都获了放。但是来某些不顶合理,既然鞋子(black40)是属人(jimmy)的,为什么人深了(即:[jimmy
release]),却还要main函数来责任烧掉他的履?(即:main函数中还是独自写一行[black40
release]) 貌似人格外的下,就连带起上的具有东西同并带,这样更有益吧。

ok,我们来改造一下Man.m中的dealloc()方法,改化下面这样:

//析构函数
-(void) dealloc
{
    NSLog(@"\"%@\"要死了! ",_name);
    [_shoe release];//这里释放_shoe
    [super dealloc];
}

就:在Man被销毁之上,先拿_shoe给销毁。这样在main()函数中,就不再要独自写一行[black40
release]来释放black40了.

今又发新景象了:jimmy交了一个吓爱人mike,二人变成了铁哥们,然后jimmy决定将好的鞋子black40,跟mike共同所有,于是main函数就成了底这样:

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

    Man *jimmy = [Man new];
    [jimmy setName:@"Jimmy"];   

    Shoe *black40 =[Shoe new];
    [black40 setColorAndSize:@"Black" shoeSize:40];

    [jimmy wearShoe:black40];

    Man *mike = [Man new];
    [mike setName:@"mike"];
    [mike wearShoe:black40];//mike跟jimmy,现在共同拥有一双40码黑色的鞋子

    [jimmy release];
    [mike release]; 

    return 0;
}

劳动来了:jimmy在挂掉的时候(即[jimmy
release]即时一行),已经顺手把好的鞋也为销毁了(也许他忘掉了mike也当过它),然后mike在老大的上,准备烧掉起曾的鞋子black40,却被告之该目标都不存在了。于是程序运行报错:

Running…
2011-02-23 13:38:53.169 MemoryManage[374:a0f] 新人”no
name”出生了!

2011-02-23 13:38:53.176 MemoryManage[374:a0f] 一对 black 35码
的履造好了!

2011-02-23 13:38:53.177 MemoryManage[374:a0f] 新人”no
name”出生了!

2011-02-23 13:38:53.179 MemoryManage[374:a0f] “Jimmy”要死了!
2011-02-23 13:38:53.181 MemoryManage[374:a0f] Black
40堆的鞋子正在给人道毁灭!

2011-02-23 13:38:53.183 MemoryManage[374:a0f] “mike”要死了!
Program received signal: 
“EXC_BAD_ACCESS”.

sharedlibrary apply-load-rules all
(gdb)

上面红色的组成部分代表程序来错了:Bad_Access也就是说访问不有的地方。

太解决之措施莫过于又回原点,Man.m的dealloc中莫系释放Shoe实例,然后将共同用底履放到main函数中,等富有人都挂掉后,最后再次销毁Shoe实例,但是估计main()函数会发生意见了:你们二单还很了,还要来辛苦我料理后事。

选这个事例仅仅就是是近水楼台先得月这样一个准绳:对于new出来的对象,使用retain造成的熏陶肯定要以相应的release抵消掉,反之亦然,否则,要么对象非会见于销毁,要么过早销毁导致后面的非法引用而失误。

下同样拨,我们来看望哪用电动释放池来换一个主意来拍卖引用计数。

相关文章