iOS内存管理机制
基本数据类型无需我们管理,内存管理的范围是对象,iOS内存管理采用了引用计数器的方式来管理内存,每个对象都有个retainCount参数,用来标识拥有该对象的拥有者个数,即有多少个正在使用该对象,当retainCount为0时,回收内存空间。
MRC下内存管理
ARC下是不允许操作retainCount参数的,新建的项目一般为ARC,需要我们手动去关闭ARC。以下均为在MRC环境下操作。
-
dealloc方法
新建对象后需要重写dealloc方法,该方法是系统自动调用的,当检测到对象的retainCount为0时自动调用dealloc方法,即对象销毁前会被调用(临终遗言),在此方法释放对象的相关资源,先释放本类的资源再调用父类的dealloc方法。 -
retainCount参数操作
对象被创建后retainCount为1,调用retain方法retainCount参数加1,调用release方法reainCount参数减1。(先介绍retain和release)
Person *person = [Person alloc] init];// person.retainCount = 1
[person retain];// person.retainCount += 1
[person release];// person.retainCount -= 1
- 内存管理原则
- 如果不再使用,就该回收(使retainCount=0)
- 若要使用对象,要对该对象retain一次(retainCount+1操作)
- 若不想使用对象,要对该对象release一次(retainCount-1操作)
- 谁创建或retain,谁release,retainCount加一减一操作总是成对出现(alloc、retain、copy和release、autorelease要成对出现)
-
野指针与内存泄露
野指针:定义的指针变量没有初始化or指向的内存空间已经被释放了
内存泄露:指向内存空间的指针变量已经被释放,而被指向的内存空间仍未被释放 - 防止野指针访问,即时置nil
Person *p1 = [Person alloc] init];
[p1 run];
[p1 release];//retainCount:0
//p1指向的对象空间已被释放,此时p1再访问就为野指针访问
[p1 run];//默认情况下不报错,需打开僵尸对象检测
//对象空间已经释放需,需要把指针变量即时置为nil,避免僵尸对象的使用
p1 = nil;
[p1 run];//对nil发送任何消息都没有效果
-
自动释放池(@autoreleasepool{...})与autorelease
autorelease与@autoreleasepool{...}搭配使用,autorelease写在自动释放池内,要不然没有效果,autorelease的作用就是在@autoreleasepool{...}内代码执行结束时给调用autorelase的对象发送一条release消息。
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];//person.retainCount == 1;
[person autorelease];//person.retainCount == 1;
//此时我们仍可以使用person对象
[person run];
//无需我们再手动releas,因为在自动释放池内代码执行完毕时,会自动release
}//person.retainCount == 0,person对象被销毁
return 0;
}
copy和mutableCopy (浅拷贝与深拷贝)
- copy出来的是不可变副本,mutableCopy出来的是可变副本
NSString *str = @"123";
NSMutableString *str2 = [str copy];
[str2 appendString:@"44444"];//此时运行会报错,因为[str copy]出来的为不可变对象
- copy有深拷贝和浅拷贝(注!自定义对象的copy就为深拷贝),浅拷贝的定义是不会产生新的副本对象,没有重新分配内存空间,只是使对象的引用计数加1(有没有重新分配内存空间是深浅拷贝的判断标准),mutableCopy均为深拷贝,会重新分配内存空间,新建一个新的副本对象。
浅拷贝演示,运行结果可以看出,NSString的copy为浅拷贝(由于iOS对字符串做了一定的优化,打印字符串的retainCount为一个很大的数,此处就不打印了)
NSString *str = [NSString stringWithFormat:@"abc"];
NSLog(@"str.address = %p",str);
NSString *str2 = [str copy];
NSLog(@"str2.address = %p",str2);

深拷贝演示,打印出来的地址是不一致的,即重新分配了内存空间
NSString *str = [NSString stringWithFormat:@"abc"];
NSLog(@"str.address = %p",str);
NSString *str2 = [str mutableCopy];
NSLog(@"str2.address = %p",str2);

@MRC中的property参数
格式:@property (参数1, 参数2) 数据类型 属性名
参数类别 | 参数 | 说明 |
---|---|---|
原子性 | atomic | 对属性加锁,多线程下线程安全,默认值 |
原子性 | nonatomic | 对属性不加锁,多线程下线程不安全 |
读写属性 | readwrite | 生成getter、setter方法,默认值 |
读写属性 | readonly | 只生成getter方法 |
set方法处理 | assign | 直接赋值,默认值 |
set方法处理 | retain | 先release旧值,再retain新值 |
set方法处理 | copy | 先release旧值,再copy新值 |
此处只讨论set方法处理参数,根据内存管理原则,当一个类关联其他对象时,即属性为对象时使用retain。assign适用基本数据类型,copy适用于字符串或代码块。
ARC下内存管理
ARC是编译器的特性,只是自动地帮我们在有需要的地方添加retain,release等代码,归根到底走的还是MRC,只是省去了我们关心retainCount参数,写一堆retain,release代码,而把更多的精力花在业务逻辑处理上。ARC下就暂时不关注retainCount参数了,换个说法,ARC下,只要没有强指针指向对象,对象就会被释放。
重写Person的delloc方法
- (void)dealloc{
NSLog(@"person dealloc!");
//ARC下不需调用父类的dealloc,也不被允许调用
}


- 指针修饰符,__strong 为强指针修饰符,__weak为弱指针修饰符(注!__是两个下划线写在一起),默认不写为强指针。
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
__strong Person *person2 = person;//__strong可以不写,默认为强指针
person = nil;//虽然person指向nil,但仍有person2强指针指向对象,此时对象不会被释放
NSLog(@"xxxxxxxx");
}//直到此刻对象才被释放
return 0;
}

Person *person = [[Person alloc] init];
__weak Person *person2 = person;
[person2 talk];
person = nil;
//person指向nil,但person2为弱指针,该句执行后对象就被释放了,对象释放后person2也被置nil
NSLog(@"xxxxxxxx");

@ARC中的property参数
ARC的property参数不能使用retain,只有strong,weak,其他的都一样。strong修饰的参数为强引用,其本质跟retain一样。weak修饰的参数为弱引用,其本质跟MRC下的assign一样,只不过只能用来修饰对象。对象属性为对象时,一般使用strong,weak可用于解决循环应用上,代理为防止循环引用就是用了weak来修饰,有时也用来修饰UI控件。
网友评论