标签:
iOS内存管理的方式是引用计数机制,分为MRC(人工引用计数)和ARC(自动引用计数)。
引用计数管理内存的理念是:通过控制内存或者对象的引用来实现生成、持有、释放、销毁对象的操作。
如果增加的次数大于减少的次数,会造成内存泄露;
如果减少的次数大于增加的次数,会造成过度释放;
如果增加的次数等于减少的次数,还继续访问,会造成野指针。
1.生成:对象的引用计数从0到1
2.持有:增加一个引用,让对象的引用计数加1
3.释放:减少一个引用,让对象的引用计数加1
4.销毁:当对象的引用计数到0时(事实上并不会为0,后面会解释),系统就会回收这个内存空间
当这块空间被系统回收之后,就不能通过指针去访问这块空间了,容易造成野指针。
注意!引用计数的概念只存在于堆区域,针对堆区的对象。
retainCount方法
可以通过改方法获取对象的引用计数的值,返回值是NSUInteger。
1.生成
+ (instancetype)alloc;在堆区域开辟一块内存空间,释放对象,并且将内存清零,同时将此对象的引用计数变为1,是从0到1的过程。
1 Hero *hero = [[Hero alloc] init];//该过程为hero对象开辟了内存空间,引用计数加1
需要注意的是,直接赋值引用计数不会增加,如下:
1 Hero *am = hero//不会引起hero对象引用计数加1
2.持有
retain;让对象的引用计数加1。
1 [hero retain];//此时hero的引用计数为2 2 [am retain];//此时hero/am的引用计数为3
3.释放
A.release;让对象的引用计数减1,而且是立即减1。
1 [hero release];//hero的引用计数为2 2 [hero release];//hero的引用计数为1 3 [hero release];//hero的引用计数为1
需要注意的是,在对hero对象进行连续三次释放操作后,我们在XCode可以通过retainCount方法查看hero的引用计数的值,结果发现并不是我们所期望的0,这是因为系统内部引用计数值没有0,0只是人与人直接方便交流和理解而引人的一个数,但从本质上来说,该对象已经被释放了,如果再去访问hero对象有可能造成野指针(被回收的内存空间被其他指针或变量占用后,再次通过原指针去访问该内存)。
即引用计数值最小是1,没有0。
B.autorelease;此方法也是让对象的引用计数减1,不过区别于release,并不是立即减1,而是在未来的某个时刻,触发减1操作。该操作与自动释放池相关。
自动释放池(autoreleasepool)
自动释放池是一个容器,来记录池子内部对象接收到的autorelease消息,哪个对象接收,接收了几次,谁先接收,谁后接收,当池子释放时,就会根据这些记录的信息来进行减1操作。
需要注意的是,自动释放池与栈区类似,遵循先进后出即先开辟空间的后被进行减1操作。
1 Hero *sf = [[Hero alloc] init]; 2 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 3 [hero autorelease]; 4 [am autorelease]; 5 [sf autorelease]; 6 [pool release];//该语句执行时,会对上述标记的指针进行减1操作,通常情况下系统会自动完成此功能
4.销毁
dealloc;从内存中销毁对象。可在类的.m文件中直接重写此方法,当对象真正从内存中销毁时会自动调用:
1 - (void)dealloc 2 { 3 NSLog("%@ is dealloc", self); 4 [super dealloc]; 5 }
可以将销毁方法和初始化方法对比一下:
初始化: 销毁:
[super init] 释放实例变量
初始化实例变量 [super dealloc]
不难发现销毁方法与初始化方法的顺序相反。因此只要重写了dealloc方法,[super dealloc]永远都在最后一行。分为两步:1.先将自身的实例变量释放掉2.执行父类中的dealloc方法,释放继承过来的实例变量。
协议
类似于Java C++里的接口对象
copy,需要类使用NSCopying协议
1.伪拷贝
特点:相当于没有拷贝,只是让外界的对象执行了一次retain操作。
1 - (id)copyWithZone:(nullable NSZone *)zone 2 { 3 return [self retain]; 4 }
2.浅拷贝
特点:拷贝的是地址,会初始化一个新的对象,但是新对象和旧对象共用一份内容,改变其中一个对象实例变量的值,另一个也会访问到改变之后的值。
注意!如果实例变量是字符串且初始化时指向了常量区,那么在拷贝后更改字符串的值,相当于在常量区又重新开辟了一个新的空间,所以这种情况下并不会影响另一个对象实例变量的值。
1 - (id)copyWithZone:(NSZone *)zone 2 { 3 //实例化一个新的对象 4 Hero *hero = [[Hero allocWithZone:zone] init]; 5 //为新对象的实例变量赋值 6 Hero.name = self.name; 7 Hero.level = self.level; 8 return hero; 9 }
3.深拷贝
特点:会重新初始化一个对象,并且内存也是两份,改变其中一个的值,另一个不会发生改变。可以理解为复制粘贴操作。
1 - (id)copyWithZone:(NSZone *)zone 2 { 3 Hero *hero = [[Hero allocWithZone:zone] init]; 4 hero.name = self.name; 5 hero.level = self.level; 6 return person; 7 }
标签:
原文地址:http://www.cnblogs.com/shvier/p/5086926.html