您的位置:首页 > 其它

day9: 内存管理初级:内存管理的方式、引用计数机制,影响计数的各个方法、dealloc方法、内存管理的基本原则

2015-08-21 19:08 671 查看
内存管理的黄金法则:

黄金法则:每次调用alloc一次,都需要调用release一次,他们两是成对出现的。

内存管理的对象:

所有NSObject下的所有OC对象,都需要做内存管理,基本类型的

内存管理初级

内存常见问题体现在两个方面:内存溢出 和 野指针异常(大部分问题都是野指针异常问题)

内存溢出:没有被释放掉的内存

野指针异常:对象内存空间已经被系统回收,仍然使用指针操作这块内存,野指针异常是程序crash的主要原因,代码量越大的程序,越难找出出现野指针的位置

了解内存管理,能帮我们提升程序性能,大大减少调试bug时间

内存管理的方式:

两种内存管理方式:ARC 和 MRC

Manual Reference Count 人工引用计数(xcode 5 之后就用自动引用计数来管理了,不建议使用MRC,若使用MRC还的手工把ARC调为NO)

Auto Reference Count 自动引用计数(ARC是基于MRC的)

现在xcode可以

引用计数机制

C语言中,使用malloc和free,进行堆内存的创建和释放,堆内存只有正在使用和销毁两种状态

实际开发中,可能会遇到,两个或两个以上的指针使用同一块内存。C语言无法记录内存使用者的个数

OC采用引用计数机制管理内存,当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时,引用计数就递减,当引用计数为零时,该对象就将释放占有的资源

影响引用计数的方法:

+alloc +1

-retain +1

-copy +1

-release -1

-autorelease -1

dealloc方法

-dealloc是继承自父类的方法,当对象引用计数为0的时候,由对象自动调用

内存管理的基本原则

自动释放池:autoreleasepool的使用

通过autoreleasepool控制autorelease对象的释放

自动释放池中是以栈的形式存在的,在池子释放的时候,会对池子里面所有的对象发送一条release消息,最后进池子的会最先收到release消息

作用:1、把对象放入到自动释放池当中 (如果不在自动释放池中发送release和autorelease是不可以的)

2、对象发送autorelease消息时,这个对象的引用计数不会立即-1,在出池子的时候才会-1;

3、一个对象只能调用一次autorelease,不能多次调用

4、autorelease的一个重要作用是在便利构造器当中使用

5、向一个对象发送autorelease消息,这个对象何时-1取决于autoreleasepool

Person.h

#import <Foundation/Foundation.h>

//想要这个类的实例可以实现copy方法,就要遵守<NSCopying>协议
@interface Person : NSObject<NSCopying>

@property(nonatomic,assign)NSString * name;
@property(nonatomic,assign)NSInteger age;

-(instancetype)initWithName:(NSString *)name andAge:(NSInteger)age;
+(instancetype)initWithName:(NSString *)name andAge:(NSInteger)age;

-(void)setName:(NSString *)name;
-(void)setAge:(NSInteger)age;

@end


Person.m

#import "Person.h"

@implementation Person
@synthesize name = _name,age = _age;

//重写父类的方法
//当对象的引用计数从0到1的过程时,由系统来自动调用
-(void)dealloc{
NSLog(@"%@ 对象已销毁",self);
//父类的销毁实现方法是必须得调用的
[super dealloc];
}

//初始化方法
-(instancetype)initWithName:(NSString *)name andAge:(NSInteger)age{
if (self = [super init]) {
_name = name;
_age = age;
}
return self;
}

//便利构造器
+(instancetype)initWithName:(NSString *)name andAge:(NSInteger)age{
Person *  p = [[Person alloc]initWithName:name andAge:age];
//autorelease的一个重要作用是在便利构造器当中使用
return  [p autorelease];
}

//NSCopying协议当中,必须实现的方法
-(id)copyWithZone:(NSZone *)zone{
Person * p = [[Person allocWithZone:zone]init];
p.name = self.name;
p.age = self.age;
return p;
}

//setter方法的内存设置
-(void)setName:(NSString *)name{
if (_name != name) {
[_name release];
_name = [name retain];
}
}

//基本数据类型  不用做内存管理
-(void)setAge:(NSInteger)age{
_age = age;
}

@end


main.m

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

int main(int argc, const char * argv[]) {
//相当于:NSAutoreleasePool * pool = [[NSAutoreleasePool alloc]init];
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");

//+alloc
Person * p = [[Person alloc]init];   //p.retainCount = 1

//得到p的引用计数(代表有多少指针指向它)
NSUInteger pCount = p.retainCount;
NSLog(@"p.retainCount = %lu",pCount);

//retain p的引用在原来的基础上+1
[p retain];                         //p.retainCount = 2
[p retain];                         //p.retainCount = 3
//release 让对象的引用计数-1
[p release];                        //p.retainCount = 2
[p release];                        //p.retainCount = 1
[p release];                        //p.retainCount = 0

//message sent to deallocated instance 野指针错误,向一个已经释放掉的对象发送了一条消息
//避免野指针错误的解决方案:[nil release];
//向一个nil发送都不会引起crash,因此当p所指向的堆内存的引用计数变为0时,p=nil;就不会引起程序的crash了
p = nil;    //保证p所指向的空间引用计数为0 的时候,在使用的时候运行不崩溃。
NSLog(@"p.retainCount = %lu",p.retainCount);

//copy
Person * p2 = [p copy];
NSLog(@"p2.retainCount = %lu",p2.retainCount);

//打印这两个地址,我们现在是拷贝出来的一个新的对象
NSLog(@"p=%p,p2=%p",p,p2);

//因为它遵守了NSCopying协议,所以可以调用copy这个方法
NSString * str = @"zero";
NSString * str2 = [str copy];
//因为在常量区所以引用计数为-1;
NSLog(@"str = %ld,str2 = %ld",str.retainCount,str2.retainCount);

//autorelease 它的引用计数不会立即-1
Person * p3 = [[Person alloc]init];
[p3 autorelease];
NSLog(@"p3.retainCount = %lu",p3.retainCount);

@autoreleasepool {
//接收autorelease消息的对象会被放入离它最近的自动释放池
Person * p7 = [[[Person alloc]init]autorelease];
NSLog(@"p7 = %ld",p7.retainCount);

//用构造方法 初始化的对象
Person * p8 = [Person initWithName:@"zero" andAge:18];
NSLog(@"p8.retainCount = %ld",p8.retainCount);
//            [p8 release];
p8 = nil;
NSLog(@"p8.retainCount = %ld",p8.retainCount);

}

//----------copy--------------
//浅拷贝:拷贝的是地址,也就是不会申请一块新的内存空间,源对象的内存空间的引用计数+1;
//深拷贝:拷贝的是内容,也就是会申请一块新的内存空间,并把源内存空间中的内容拷贝进去,所以源内存空间的引用计数还是1,新内存空间的引用计数由0变为1;深拷贝拷贝出来的是一个可变的字符串对象。

//        把源对象的内容复制一份,在堆内存申请一块新内存空间,把复制的内容粘贴进去(拷贝的是内容)

//浅拷贝 copy
NSString * str5 = [[NSString alloc]initWithFormat:@"popo"];
NSString * str6 = [str5 copy];
NSLog(@"str5 = %@ str6 = %@",str5,str6);
NSLog(@"str5 = %p str6 = %p",str5,str6);
NSLog(@"str5.retainCount = %lu str6.retainCount = %lu",str5.retainCount,str6.retainCount);

//深拷贝 mutableCopy
NSMutableString * str7 = [str5 mutableCopy];
NSLog(@"str5 = %p,str7 = %p",str5,str7);
NSLog(@"str5.retainCount = %lu str7.retainCount = %lu",str5.retainCount,str7.retainCount);

NSMutableString * mstr = [[NSMutableString alloc]initWithFormat:@"浩浩"];

NSString * mstr2 = [mstr copy];
NSLog(@"mStr = %p,  mStr2 = %p",mstr,mstr2 );

NSMutableString * mStr3 = [mstr mutableCopy];
[mStr3 appendFormat:@"你妹呢?"];

NSLog(@"%@",mStr3);
NSLog(@"mStr = %p,mStr3 = %p",mstr,mStr3);
NSLog(@"%lu,%lu",mstr.retainCount,mStr3.retainCount);
/*
1、只有不可变字符串跟copy才是钱拷贝,其他三种都是深拷贝
2、不管源字符串是否可变,只要用mutableCopy 拷贝出来的字符串都是可变的
*/

Person * p5 = [Person initWithName:@"波波" andAge:18];
Person * p6 = [p5 copy];
p6.name = @"浩浩";
p6.age = 20;
NSLog(@"p5.retainCount = %lu,p6.retainCount = %lu",p5.retainCount,p6.retainCount);
NSLog(@"p5 - %@,%ld",p5.name,p5.age);
NSLog(@"p6 - %@,%ld",p6.name,p6.age);

//这里的{相当于:[pool release];
}

//创建池子
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc]init];

//创建三个对象放入到池子当中
Person * p4 = [[Person alloc]init];
Person * p5 = [[Person alloc]init];
Person * p6 = [[Person alloc]init];

//向一个对象发送autorelease消息,这个对象何时-1取决于autoreleasepool
[p4 autorelease];
[p5 autorelease];
[p6 autorelease];
//    [p6 autorelease]; //引用计数为0的时候不能再次释放

//打印这三个对象的地址
NSLog(@"p4 = %@",p4);
NSLog(@"p5 = %@",p5);
NSLog(@"p6 = %@",p6);

//释放池子
[pool release];

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: