OC内存管理
一、基本原理
(一)为什么要进行内存管理。
由于移动设备的内存极其有限,所以每个APP所占的内存也是有限制的,当app所占用的内存较多时,系统就会发出内存警告,这时需要回收一些不需要再继续使用的内存空间,比如回收一些不再使用的对象和变量等。
管理范围:任何继承NSObject的对象,对其他的基本数据类型无效。
本质原因是因为对象和其他数据类型在系统中的存储空间不一样,其它局部变量主要存放于栈中,而对象存储于堆中,当代码块结束时这个代码块中涉及的所有局部变量会被回收,指向对象的指针也被回收,此时对象已经没有指针指向,但依然存在于内存中,造成内存泄露。
(二)对象的基本结构
每个OC对象都有自己的引用计数器,是一个整数,表示对象被引用的次数,即现在有多少东西在使用这个对象。对象刚被创建时,默认计数器值为1,当计数器的值变为0时(原理就是你不引用它了,所以才把它释放掉,如果你还引用他,你释放了就会造成野指针),系统自动调用dealloc方法回收对象空间(一般会重写dealloc方法),在空间回收之前,将保有的其他对象的所有权给释放掉,例子见下:
-(void)dealloc
{
当该类对象的引用计数为0时,会自动调用该类的dealloc方法
当调用dealloc方法时,该对象A的空间将要被系统回收,在空间回收之前,将该对象保有的对象B的所有权给释放掉
[_tea release];//_tea指向的那个对象B的引用计数减1,因为对象A已经被回收,_tea也被回收了,它已经不会对B有所有权了.
[super dealloc];
}
在每个OC对象内部,都专门有4个字节的存储空间来存储引用计数器(变量)。
(三)内存管理原则
如果你对一个对象进行alloc,retain,copy之后,你就拥有了该对象的所有权,你就必须对它进行release或者autorelease
(1)原则
只要还有人在使用某个对象,那么这个对象就不会被回收;
只要你想使用这个对象,那么就应该让这个对象的引用计数器+1;
当你不想使用这个对象时,应该让对象的引用计数器-1;
(2)谁创建,谁release
1如果你通过alloc,new,copy来创建了一个对象,那么你就必须调用release或者autorelease方法
2不是你创建的就不用你去负责
(3)谁retain,谁release
只要你调用了retain,无论这个对象时如何生成的,你都要调用release
(四)总结
有始有终,有加就应该有减。曾经让某个对象计数器加1,就应该让其在最后-1.
(四)@property的参数
(1)内存管理相关参数
Retain:对对象release旧值,retain新值(适用于OC对象类型)
Assign:直接赋值(默认,适用于非oc对象类型)
Copy:release旧值,copy新值
(2)是否要生成set方法(若为只读属性,则不生成)
Readonly:只读,只会生成getter的声明和实现
Readwrite:默认的,同时生成setter和getter的声明和实现
(三)引用计数器的作用
判断对象要不要回收的唯一依据就是计数器是否为0,为0则回收。
- (void)setTea:(Teacher *)tea
{
//self.tea = tea;
//[self setTea:tea];等价会造成死循环
//判断原有对象和新对象是否是同一个,如果是同一个就没有必要在重新赋值了,否则会先release,release之后空间被系统回收,此时在retain就成为野指针问题了
if (_tea != tea) {
[_tea release];//释放之前对象保有的所有权
//让实例变量_tea 保有新的所有权
_tea = [tea retain];
}
}
-(Teacher *)tea
{
return [[_tea retain]autorelease];
}
//语义特性声明为copy,setter方法内部实现
-(void)setTea1:(Teacher *)tea1
{
if (_tea1 != tea1) {
[_tea1 release];
//如果相对一个对象进行copy操作,对象的类必须服从一个NSCopy协议,并且实现协议的方法
@interface Teacher : NSObject<NSCopying>
_tea1 = [tea1 copy];
}
}
-(void)dealloc
{
[_tea release];
[super dealloc];
}
- (id)copyWithZone:(NSZone *)zone
{
Teacher *newTea = [[Teacher allocWithZone:zone]init];
newTea.name = self.name;
newTea.gender = self.gender;
return newTea;
}
-(id)initWithName:(NSString *)name gender:(NSString *)gender
{
if (self = [super init]) {
self.name = name;//使用setter方法retain下对所有权先保留一份
self.gender = gender;
}
return self;
}
+(id)teacherWithName:(NSString *)name gender:(NSString *)gender
{
Teacher *teacher = [[Teacher alloc]initWithName:name gender:gender];
//[teacher release];
return [teacher autorelease];有alloc,有autorelease
}
-(void)dealloc
{
[_name release];
[_gender release];
NSLog(@"回收了");
[super dealloc];
}
//便利构造器内存管理
// Teacher *tea = [Teacher teacherWithName:@"Frank" gender:@"man"];
// NSLog(@"%lu",[tea retainCount]);
// NSLog(@"fan");
//collection 的内存管理
Teacher *tea1 = [[Teacheralloc]initWithName:@"xaiogaung"gender:@"man"];
//0 -> 1
Teacher *tea2= [[Teacheralloc]initWithName:@"xaiomeng"gender:@"man"];
//0 -> 1
Teacher *tea3 = [[Teacheralloc]initWithName:@"xaiojian"gender:@"man"];
//0 -> 1
//NSMutableArray *arr = [NSMutableArray arrayWithObjects:tea1,tea2,tea3, nil];
NSMutableArray *arr = [[NSMutableArrayalloc]initWithObjects:tea1,tea2,tea3,nil];
//挡把一个对象放入集合中(数组,字典,集合)中时,会将对象的引用计数加1,因为内部做了retain操作
NSLog(@"%lu",[tea1retainCount]);
NSLog(@"%lu",[tea2retainCount]);
[tea1release];
[tea2release];
//[arr release];
//2.当collection(数组,字典,集合),空间被回收时,他们会向容器中的每一个元素都发送一个release消息(对应添加元素时的retain操作)
//3.当从collection(数组,字典,集合)中移除一个元素时,会release该对象,引用计数减1
[arr removeAllObjects];
[arrrelease];
//setter以及 getter方法,内不是对实例变量赋值以及实例变量的取值操作,所以方法没要操作实例变量