内存管理

当我们创建一个对象时,我们可能在一段时间内,访问这个对象的成员变量,调用这个对象的方法。但当我们不再需要这个对象的时候,就希望系统回收该对象所占用的内存。

在Objective C中,采用引用计数(Reference Counting)来跟踪对象的状态。每个对象都有一个与之关联的整数,叫做引用计数器。当访问某个对象时,该对象引用计数+1,当不再访问该对象的时候,引用计数-1。当对象的引用计数为0时,表示系统不再使用这个对象,系统回收该对象所占内存。


系统销毁一个对象之前,会调用该对象的dealloc方法。如果对象还持有其他对象的引用,此时要重写dealloc方法,在该方法中释放所持有的其他对象(一般是调用被持有对象的release方法)。


悬空指针(Dangling Pointer):当一个对象被销毁,该对象就不再存在,如果此时有一个指针指向这个被销毁的对象,这个指针就叫做悬空指针。调用悬空指针所指向对象的方法时,会出现未知结果!


有关引用计数的方法:

1,retain 引用计数+1

2,release 引用计数-1

3,autorelease 不改变引用计数的值,只是将对象放入自动释放池中

4,retainCount 返回对象的引用计数值


手动引用计数中,改变对象引用计数方式有几种:

1,调用alloc  new  copy  mutableCopy 方法创建对象时,该对象引用计数+1

2,调用对象retain 方法,引用计数+1

3,调用对象release方法,引用计数-1


下面写一个实例来进行手动内存管理,新建一个工程,把自动引用计数(Automatic Reference Counting)关掉

//首先,我们创建一个Phone类,类中有一个实例变量:价格_price
//  Phone.h


#import <Foundation/Foundation.h>

@interface Phone : NSObject {
    NSUInteger _price;
}
@property (assign,nonatomic) NSUInteger price;
@end


//
//  Phone.m
//  手动管理内存
//

#import "Phone.h"

@implementation Phone
//重写一下dealloc方法,这样当一个对象被销毁之前他会调用dealloc方法,我们就可以看到了
-(void)dealloc {
    NSLog(@"phone dealloc.");
    [super dealloc];
}

@end

//再创建一个Person类,他拥有一个手机(就是有一个Phone类型的成员变量)
//  Person.h
//  手动管理内存

#import <Foundation/Foundation.h>
@class Phone;

@interface Person : NSObject {
    Phone *_phone;
}

-(void)setPhone:(Phone *)phone;
-(Phone *)phone;

@end


//
//  Person.m
//  手动管理内存


#import "Person.h"

@implementation Person
//这里setter方法有问题,留在下面讲!!!
-(void)setPhone:(Phone *)phone {
    [phone retain];//这里要将Phone类型的值赋给Person的成员变量_phone,将phone的引用计数加一
    _phone=phone;
}

-(Phone *)phone {
    return _phone;
}

//手动管理内存原则:谁(对象或者方法)把一个对象的引用计数+1,谁就要负责‘死前’将该对象引用计数-1
-(void)dealloc {
    [_phone release];//创建时引用计数加1,这里对应的减1
    NSLog(@"person dealloc.");
    [super dealloc];
}

@end

//
//  main.m
//  手动管理内存


#import <Foundation/Foundation.h>
#import "Person.h"
#import "Phone.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Phone *iPhone=[[Phone alloc] init];//这里用了alloc方法创建对象iPhone,该对象引用计数+1
        NSLog(@"iPhone创建:%ld",iPhone.retainCount);
        
        Person *me=[[Person alloc] init];//创建me对象,将该对象引用计数+1
        NSLog(@"me:创建%ld",me.retainCount);
        [me setPhone:iPhone];//我们写了setPhone方法,把iPhone 对象引用计数+1
        NSLog(@"iPhone赋值:%ld",iPhone.retainCount);
        
        [iPhone release];//掉用release方法,引用计数-1
        NSLog(@"iPhone release: %ld",iPhone.retainCount);
        
        [me release];//这里释放me对象,引用计数-1。现在me的引用计数为0:第一步,调用dealloc方法(我们重写了此方法,在此方法中iPhone对象调用release,引用计数-1);第二步,销毁对象
    }
    return 0;
}

运行结果如下:

2015-08-25 23:25:35.164手动管理内存[2099:141996] iPhone创建:1

2015-08-25 23:25:35.165手动管理内存[2099:141996] me:创建1

2015-08-25 23:25:35.165手动管理内存[2099:141996] iPhone赋值:1

2015-08-25 23:25:35.166手动管理内存[2099:141996] phone dealloc.

2015-08-25 23:25:35.166手动管理内存[2099:141996] iPhone release: 1

2015-08-25 23:25:35.166手动管理内存[2099:141996] person dealloc.



至此,我们完成了一次手动管理内存!
现在,再考虑一个问题,在调用setPhone方法时,如果我们再传入一个Phone对象,并赋值..........
//
//  main.m
//  手动管理内存


#import <Foundation/Foundation.h>
#import "Person.h"
#import "Phone.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Phone *iPhone=[[Phone alloc] init];//这里用了alloc方法创建对象iPhone,该对象引用计数+1
        NSLog(@"iPhone创建:%ld",iPhone.retainCount);
        
        Person *me=[[Person alloc] init];//创建me对象,将该对象引用计数+1
        NSLog(@"me:创建%ld",me.retainCount);
        [me setPhone:iPhone];//我们写了setPhone方法,把iPhone 对象引用计数+1
        NSLog(@"iPhone赋值:%ld",iPhone.retainCount);
        
        //重新创建一个Phone对象,给me的成员变量_phone赋值
        Phone *newPhone=[Phone new];//newPhone +1
        [me setPhone:newPhone];
        NSLog(@"iPhone count :%ld",iPhone.retainCount);//iPhone count 仍是2
        NSLog(@"newPhone count :%ld",newPhone.retainCount); //newPhone +1
        
        [newPhone release];//newPhone -1
        
        [iPhone release];//掉用release方法,引用计数-1
        NSLog(@"iPhone release: %ld",iPhone.retainCount);
        
     // 有了newPhone后,这里会将newPhone对象调用release,而iPhone不调用,造成内存泄漏!!!!!!
        [me release];//这里释放me对象,引用计数-1。现在me的引用计数为0:第一步,调用dealloc方法(我们重写了此方法,在此方法中iPhone对象调用release,引用计数-1);第二步,销毁对象
   
    }
    return 0;
}
运行结果如下:

2015-08-25 23:50:37.532手动管理内存[2193:150238] iPhone创建:1

2015-08-25 23:50:37.533手动管理内存[2193:150238] me:创建1

2015-08-25 23:50:37.533手动管理内存[2193:150238] iPhone赋值:2

2015-08-25 23:50:37.534手动管理内存[2193:150238] iPhone count :2

2015-08-25 23:50:37.534手动管理内存[2193:150238] newPhone count :2

2015-08-25 23:50:37.534手动管理内存[2193:150238] iPhone release: 1

2015-08-25 23:50:37.534手动管理内存[2193:150238] phone dealloc.

2015-08-25 23:50:37.534手动管理内存[2193:150238] person dealloc.




为了避免这种问题的出现,在setPhone方法中,将原来的_phone的引用计数减一,修改如下:
-(void)setPhone:(Phone *)phone {
//    [phone retain];//这里要将Phone类型的值赋给Person的成员变量_phone,将phone的引用计数加一
//    _phone=phone;
    if (_phone!=phone) {//两次传入相同的对象,没有什么意义,所以这里加一个判断!
        [_phone release];
        _phone=[phone retain];
    }
}

运行结果如下:

2015-08-25 23:49:33.026手动管理内存[2182:149663] iPhone创建:1

2015-08-25 23:49:33.028手动管理内存[2182:149663] me:创建1

2015-08-25 23:49:33.028手动管理内存[2182:149663] iPhone赋值:2

2015-08-25 23:49:33.028手动管理内存[2182:149663] iPhone count :1

2015-08-25 23:49:33.028手动管理内存[2182:149663] newPhone count :2

2015-08-25 23:49:33.028手动管理内存[2182:149663] phone dealloc.

2015-08-25 23:49:33.028手动管理内存[2182:149663] iPhone release: 1

2015-08-25 23:49:33.029手动管理内存[2182:149663] phone dealloc.

2015-08-25 23:49:33.029手动管理内存[2182:149663] person dealloc.






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值