标签:
为什么要进行内存的管理呢?
iOS程序会出现Crash(闪退)问题,90%以上都是因为内存问题.
内存问题体现在两个方面:内存溢出 、野指针异常.
内存溢出: iOS会给每个应用程序提供一定的内存,用于程序运行.而一旦超出了内存上限,程序就会Crash.
野指针异常: 对象内存空间已经被系统回收, 却仍然使用指针操作这块内存.
一. 垃圾回收(gc):java常见的管理内存的管理内存的方法,由系统自动来检测对象是否被使用,是否被释放.
二. MRC(Manual Reference Count): 手动管理引用计数,iOS管理内存方式,程序员通过手动方式来管理对象是否被释放.
三. ARC(Auto Reference Count): 自动管理引用计数,基于MRC,系统自动的管理内存,以后还是先使用MRC,培养管理内存的习惯.
iOS支持两种内存管理方式: MRC & ARC
MRC的内存管理机制是: 引用计数.
ARC是基于MRC的.
C语言中, 是使用malloc 和 free,进行堆内存的创建和释放.堆内存只有正在使用和销毁两种状态.
而实际开发中,可能会遇到两个以上的指针使用同一块内存.C语言无法记录内存使用者的个数.
OC采用引用计数的机制管理内存,当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时,引用计数就递减.当引用计数到0时,该对象就释放掉占有的资源.
开辟内存空间,让被开辟的内存空间的引用计数变为1. 这是由 0 变为 1 的过程.
//对象被创建出来之后它的引用计数retainCount就变成1
BOY *bo = [[BOY alloc] init];
NSLog(@"%ld",bo.retainCount);//1
引用计数+1,如果内存空间之前的引用计数为1,retain之后变为2,如果引用计数是3,retain之后便为4.
//retain :对对象引用计数+1
[bo retain];
NSLog(@"%ld",bo.retainCount);//2
[bo retain];
[bo retain];
NSLog(@"%ld",bo.retainCount);//4
把某一内存区域的内容拷贝一份,拷贝到新的内存空间里去,被拷贝区域的引用计数不变,新的内存区域的引用计数为1.
引用计数减1,如果内存空间之前的引用计数为4,release之后变为3,如果之前引用计数为1,release之后计数为0,内存被系统回收.
// release:对对象的引用计数-1
[bo release];
NSLog(@"%ld",bo.retainCount);//3
[bo release];
[bo release];
[bo release];
// 当对象的引用计数从1 -> 0 的时候,会自动调用dealloc方法,dealloc才是对应对象释放的方法.
NSLog(@"%ld",bo.retainCount);//1
// 当对象调用release的时候它的引用计数是1时,就不再进行-1操作,而是直接调用dealloc.
dealloc是继承父类的方法,当对象引用计数为0的时候,由对象自动调用.
我们可以在dealloc中打印一句话,以验证对象引用计数是否降为0.
- (void)dealloc{
NSLog(@"对象被释放了");
[super dealloc];
}
在未来某一时刻引用计数减1. 如果内存之前引用计数为4,autorelease之后仍为4,在未来某个时刻会变成3.
通过 autoreleasepool 控制 autorelease对象的释放.
向一个对象发送autorelease消息,这个对象何时释放,取决于autoreleasepool.
autoreleasepool的使用:
BOY *boy = [[BOY alloc] init];
[boy retain];
[boy retain];
NSLog(@"%ld",boy.retainCount);//3
//release 马上会把对象的引用计数-1,但是autorelease 会延迟对对象的计数-1.
[boy release];//2
NSLog(@"%ld",boy.retainCount);//2
// 自动释放池
// 只要对象用autorelease释放会把对象放入系统的自动释放池中,等出了池子的范围,对象引用技术自动-1,这个相当于java的垃圾回收,对象释放.
@autoreleasepool {
[boy autorelease];
NSLog(@"%ld",boy.retainCount);//2
}
NSLog(@"%ld",boy.retainCount);//1
以下看看NSArray / NSString / NSMutableString / NSDictionary 的引用计数.其中,NSString是特殊的.
NSArray *arr = @[@"1",@"2",@"3",@"4"];
NSLog(@"%ld",arr.retainCount);//1
NSString *str = @"11111";
NSLog(@"%ld",str.retainCount);
// -1,代表正整数最大值,因为NSString在全局静态区.
//NSString 的对象在全局静态区,它的引用计数是-1,代表正整数最大值.
NSMutableString *str2 = [NSMutableString stringWithString:@"222222"];
NSLog(@"%ld",str2.retainCount);//1
NSDictionary *dic = @{@"1":@"2",@"3":@"4"};
NSLog(@"%ld",dic.retainCount);//1
引用计数的增加与减少相等, 当引用计数降为0之后, 不应该再使用这块内存.
凡是使用了alloc 、retain 或者copy 让内存的引用计数增加了,就需要使用release 或者autorelease让内存的引用计数减少.在一段代码中,增加和减少的次数要相等.
使用便利构造器之后,不需要对它的内存做管理,因为在返回对象时会加上一个autorelease.
NSArray *arr1 = [[NSArray alloc] initWithObjects:@"1",@"2", nil];
NSArray *arr2 = [NSArray arrayWithObjects:@"1",@"2", nil];
[arr1 release];
与-retain 不同, 一个对象要想copy,生成自己的副本,需要实现NSCopying协议,定义copy的细节(如何copy).如果类没有接受NSCopying协议而给对象发送copy消息,会引起crash.
系统的类要是实现拷贝功能,必须签订拷贝NSCopying协议,然后实现对应方法.
不可变数组/字典可以通过mutableCopy转换成可变的数组字典.
mutableCopy 拷贝出来的对象是可变的,copy是不可变的.
不可变数组:
NSArray *arr = @[@"2",@"3",@"4",@"5"];
NSLog(@"%ld",arr.retainCount);
NSArray *newArr = [NSArray arrayWithArray:arr];
NSLog(@"%@",newArr);
NSLog(@"%ld",newArr.retainCount);
不可变 -> 可变数组:
NSMutableArray *arr1 = [NSMutableArray arrayWithArray:arr];
NSMutableArray *arr2 = [arr mutableCopy];
[arr2 addObject:@"6"];
NSLog(@"%@",arr2);
不可变 -> 可变字典:
NSDictionary *dic = @{@"2":@"3"};
NSMutableDictionary *mudic = [dic mutableCopy];
NSLog(@"%@",mudic);
如果自己的类想要实现copy功能,就必须先签订NSCopying,然后实现对应的协议方法,initWithZone,之后就可使用copy.
BOY.h 文件
@interface BOY : NSObject<NSCopying>
@property(nonatomic, retain)NSString *name;
@property(nonatomic, copy)NSString *hobby;
@end
copy方法的实现:
BOY.m 文件
- (id)copyWithZone:(NSZone *)zone{
BOY *b = [BOY allocWithZone:zone];
b.name = _name;
b.hobby = _hobby;
return b;
}
copy方法的实现:
main.m 文件
BOY *boy = [BOY boyWithName:@"小新" hobby:@"飞"];
BOY *newBoy = [boy copy];
NSLog(@"%@",newBoy.name);
//boy newBoy所对应的retainCount
NSLog(@"%ld",boy.retainCount); // 1
NSLog(@"%ld",newBoy.retainCount);// 1
//!!!!copy也可以改变引用计数,但是他改变的是新对象的引用计数,而原来对象的计数不变.
BOY *boy1 = [[BOY alloc] init];
NSLog(@"%ld",boy1.retainCount);//1
NSMutableArray *arr = [NSMutableArray arrayWithObjects:boy1, nil];
NSLog(@"%ld",[arr[0] retainCount]);//2
NSLog(@"%ld",boy1.retainCount);//2
[arr removeObjectAtIndex:0];
NSLog(@"%ld",boy1.retainCount);//1
// 当对象放入到容器Array 或 字典中时,对象会被容器进行一次持有,就是retain一次,它的引用计数会+1,主要是为了防止空指针问题.
//等对象从容器中移除时,相应地会-1.
// 若boy已经被释放掉, 只剩有在arr中,它会随着arr消失而消失.
BOY *boy = [[BOY alloc] init];
NSArray *arr = @[boy];
[boy release];
NSLog(@"%ld",boy.retainCount);
BOY.m文件
// 自定义初始化.使用setter方法 :将_name 改为 self.name; _hobby 改为 self.hobby
- (instancetype)initWithName:(NSString *)name hobby:(NSString *)hobby{
self = [super init];
if (self) {
self.name = name;
self.hobby = hobby;
}
return self;
}
// 便利构造器. 添加autorelease , 所以若是在main.m函数中发现使用便利构造器定义出的对象,不需要再release,它的内部已经实现.
+ (BOY *)boyWithName:(NSString *)name hobby:(NSString *)hobby{
BOY *b = [[BOY alloc] initWithName:name hobby:hobby];
//写便利构造器时别忘了autorelease.
return [b autorelease];
}
//!!setter方法的内部,如何实现内存优化.
- (void)setName:(NSString *)name{
if (_name != name) {
[_name release];
_name = [name retain];
}
//第二种写法:
// [name retain];
// [_name release];
// _name = name ;
}
// dealloc 完整版
- (void)dealloc{
NSLog(@"对象被释放了");
// 把成员变量中的+1操作全部减去,不使用 . 就直接对他释放,没有延迟释放的问题.
[_name release];
[_hobby release];
[_girls release];
[super dealloc];
}
在BOY.h 中添加一条属性
@property (nonatomic, retain)GIRL *girls;
main.m文件:
BOY *b = [[BOY alloc] init];
GIRL *girl = [[GIRL alloc] init];
b.girls = girl;
NSLog(@"%ld",girl.retainCount);//2
GIRL *girl2 = [[GIRL alloc] init];
b.girls = girl2;
NSLog(@"%ld",girl.retainCount);//1
// 因为第一个是girl给了b,而当第二个girl2给了b时,因为setter方法的内部实现,girl 与 girl2 不同, 所以就将girl释放了,它就变成1了.
Person.h 文件:
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property(nonatomic, retain)NSMutableArray *arr;
@property(nonatomic, copy)NSString *name;
@property(nonatomic, assign)NSInteger age;
//自定yi初始化
- (id)initWithName:(NSString *)name age:(NSInteger)age;
+ (Person *)personWithName:(NSString *)name age:(NSInteger)age;
@end
Person.m文件:
#import "Person.h"
@implementation Person
- (id)initWithName:(NSString *)name age:(NSInteger)age{
self= [super init];
if (self) {
self.name = name;
_age = age; // age 是NSInteger型,保存在栈区,不影响堆区内存.
// 当对象创建好之后,里面的数组也会创建好,不用在外部创建,避免因为忘了而造成问题.
self.arr = [NSMutableArray array];
}
return self;
}
+ (Person *)personWithName:(NSString *)name age:(NSInteger)age{
Person *per = [[Person alloc] initWithName:name age:age];
return [per autorelease];
}
// 补上一个dealloc
- (void)dealloc{
[_arr release];
[_name release];
[super dealloc];
}
@end
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:
原文地址:http://blog.csdn.net/gao_zi/article/details/47110973