0、概念:
持有与引用:
对象的持有和引用的区别,持有必定引用,引用不一定持有,引用只是保存了对象所在的内存空间地址,系统可以随时释放该内存空间,给其他它程序用;持有则是,拥有这个内存空间的所有权,系统无权释放。例如由房产证的的人和没有房产证的人,它们都知道房子的地址(引用了该地址),但是有房产证的人,政府(操作系统)无权力收回该房子,无房产证的人,政府有权力收回该房子。
释放与废弃:
释放对象表示指针放弃对对象的持有权,即MRC下调用release,ARC下指针置为nil;废弃对象表示系统收回对象所占用的内存空间。
1、OC下的内存管理原则:
- 存放在栈区和常量区的数据所占用的内存,由系统负责回收。
- 存放在堆区的数据所占用的内存,由用户自己负责回收。如果不会回收就会造成内存的泄漏。
- 一般内存的泄漏,指的是堆区中的内存没有被释放。
- 内存的管理通过引用计数器(记录还有多少引用持有该对象)来管理的,对象的释放并不是自动的,如果是自动的话那叫垃圾回收,使用过release方法来回收。
2、release、alloc、retain、autorelease方法介绍
release方法中的逻辑
-(void)release{
if(对象的引用计数器 == 0){
释放内存空间;
}else{
对象引用计数器--;
}
}
3、MRC规则
- 自己创建的对象自己释放,即使用alloc、new、copy、mutableCopy方法创建的对象,对应的引用持有该对象,需要自己调用release释放。
- 以alloc、new、copy、mutableCopy开头的方法(allocPerson、newPerson)创建的对象,对应的引用也持有该对象,需要自己调用release释放。
- 不是以上述两种方式创建的对象,对应的引用不持有该对象,不需要自己调用release释放,想要使用,需要先调用retain。(例如NSArray的array方法)。
- 对于使用第三条创建的对象,需要使用autoReleasePool(自动释放池)来释放该对象。
- 如果想要使用该对象,首先要持有它,即调用retain方法。
- 如果已经持有该对象的引用,不想要再使用了,需要调用release方法取消对对象的持有权。
--------------------Person类-------------------------------- @interface Person : NSObject +(instancetype)person; @end @implementation Person +(instancetype)person { // 将创建的对象加入到栈顶的自动释放池中。若没有自动释放池,则该对象无法释放,导致内存泄漏。 return [[[Person alloc] init] autorelease]; } @end --------------------Man类-------------------------------- @interface Man : NSObject +(instancetype)allocMan; @end @implementation Man +(instancetype) allocMan { return [[Man alloc] init]; } @end --------------------main类-------------------------------- #import "Person.h" #import "Man.h" int main(int argc, const char * argv[]) { /* *没有使用alloc、new、copy、mutableCopy开头的方法创建的对象。 *指针str1、str2、str3不会持有这三个对象,即不需要p调用release方法释放该对象,即使调用了release也释放不了。 *下面的代码中,虽然每个对象调用了release方法,但是内存还是暴涨,对象没有释放掉。 */ for (int i = 0; i<999999; i++) { Person *str1 = [Person person]; Person *str2 = [Person person]; Person *str3 = [Person person]; NSLog(@"%@===%@===%@", str1, str2, str3); [str1 release]; [str2 release]; [str3 release]; } // 使用自动释放池,每次循环开始创建一个自动释放池,每次循环结束,自动释放池销毁,并向str1、str2、str3三个对象发送release方法。内存不暴涨了。 for (int i = 0; i<999999; i++) { @autoreleasepool{ Person *str1 = [Person person]; Person *str2 = [Person person]; Person *str3 = [Person person]; // str1、str2、str3三个引用没有持有这三个对象,想要使用该对象,需要先持有。 [str1 retain]; [str2 retain]; [str3 retain]; NSLog(@"%@===%@===%@", str1, str2, str3); } } // 使用alloc、new、copy、mutableCopy方法创建的对象,str1、str2、str3持有各自的对象,调用release,会释放创建的三个对象对应的内存空间,内存不暴涨。 for (int i = 0; i<999999; i++) { Person *str1 = [[Person alloc] init]; Person *str2 = [[Person alloc] init]; Person *str3 = [[Person alloc] init]; NSLog(@"%@===%@===%@", str1, str2, str3); [str1 release]; [str2 release]; [str3 release]; } // 使用以alloc、new、copy、mutableCopy单词开头的方法创建的对象,str1、str2、str3持有各自的对象,调用release,会释放创建的三个对象对应的内存空间,内存不暴涨。 for (int i = 0; i<999999; i++) { Man *str1 = [Man allocMan]; Man *str2 = [Man allocMan]; Man *str3 = [Man allocMan]; NSLog(@"%@===%@===%@", str1, str2, str3); [str1 release]; [str2 release]; [str3 release]; } }
总结:
- 由此可见,在对象没有其它指针引用的情况下,内存的释不释放,关键看创建对象时,是否调用了autorelease方法,如果调用了该方法,则需要使用自动释放池调用release方法才能释放内存空间。
- Person *p = [[[Person alloc] init] autorelease]; p指针没有持有该对象,栈顶的自动释放池持有该对象,当自动释放池销毁时,会向所有池中的对象发送release方法,如果没有使用自动释放池包裹,会发生内存泄漏。
4、MRC机制下的代码写法
int main(int argc, const char * argv[]) { // 使用alloc、new、copy、mutableCopy方法创建的对象,引用p已经持有该对象,无需调用retain。 Person *p = [[Person alloc] init]; NSLog("%@", p); // 不是用alloc、new、copy、mutableCopy方法创建的对象,引用p没有持有该对象,需要调用retain,才能使用。 Person *p1 = [Person person]; [p1 retain]; NSLog("%@", p1); } -------------------------Person类------------------ @interface Person : NSObject @property (nonatomic, copy) NSString *name; +(instancetype)person; +(instancetype)allocPerson; @end @implementation Person // 方法名没有使用alloc、new、copy、mutableCopy开头的方法,创建对象时需要调用autorelease。 +(instancetype)person { return [[[Person alloc] init] autorelease]; } // 方法名使用alloc、new、copy、mutableCopy开头的方法,创建对象时不需要调用autorelease。 +(instancetype)allocPerson { return [[Person alloc] init]; } // 两种方式创建对象的区别在于有无调用autorelease,即是否需要使用自动释放池来调用一次release方法。 // name的set方法,取消属性当前引用对象的持有权,添加新对象的持有权。 - (void)setName:(NSString *)name { if (_name) { [_name release]; } _name = name; [_name retain]; }