您的位置:首页 > 移动开发 > Objective-C

内存管理

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

在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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息