一、野指针与僵尸对象
- 僵尸对象:已经被释放的对象。 再使用对象就会报错。
- 野指针:一个指针指向被释放的对象的内存。
二、自动释放池 autoreleasepool
前提:相对于MRC下,一次 retain / alloc 对应一次release,经常需要手动release进行内存管理,防止内存泄漏。从而引出自动释放池
- 自动释放池
自动释放池是以栈为节点通过双向链表组合而成,与线程一一对应
// autoreleasepool 自动释放池
@autoreleasepool { //1.创建一个自动释放池
Person *p = [[Person alloc]init autorelease]; // 2. 把对象放在池子里
[p run];
} // 自动释放池被销毁,所有对象被release一次,p被释放。
实现原理:
autorelease 经过编译器后被改写为objc_autoreleasepoolPush 和 objc_autoreleasepoolPop
objc_autoreleasepoolPush内部会调用C++的autoreleasePage::push方法
objc_autoreleasepoolPop内部会调用C++的autoreleasePage::pop方法
一个对象如array何时被释放?
在每次runLoop即将结束时调用autoreleasePage::pop方法,把array对象调用release方法进行释放。
注意点:
1)对象调用autoRelease方法,仅仅是将对象放到引用释放池,引用计数不会改变。只有当自动释放池被销毁,池内对象引用计数才会 -1。
2)自动释放池被销毁,所有对象仅仅是被release一次,并不代表被释放。只有对象引用计数值为0,才算被释放。
3)自动释放池可以有多个,并且自动释放池可以嵌套。
多个自动释放池以栈的形式存储。 当对象调用autorelease方法,是将对象放在栈顶的释放池中
应用场景:
可以使用在for循环加载内存较大的类中使用,如 for循环中alloc 图片
但是需要注意,把 autoreleasepool 写在循环内部,保证每次循环内存都可以保证释放,防止内存飙升
// 1. autoreleasepool 写在循环外部,适得其反,内存突增
@autoreleasepool{
for(int i; i<99; i++){
UIImage *image =[[[UIImage alloc]init] autorelease];
}
} // 此处才会释放,已经创建了99个对象
// 2. autoreleasepool 写在循环内部,一次循环一次对象的回收
for(int i; i<99; i++){
@autoreleasepool{
UIImage *image =[[[UIImage alloc]init] autorelease];
} // 此处释放
}
三、MRC与ARC
-
概念
MRC: manual Refrence Count,手动引用计数,程序员对对象的内存进行管理。
ARC: Auto Refrence Count 自动引用计数,编译器自动进行对象的内存管理。 -
区别
MRC 通过使用retain /release /autorelease 等方法控制对象的引用计数值,从而实现对象的内存管理。当引用计数值为0,对象被释放。
ARC 通过编译器与runtime合作自动进行对象的内存管理,只要有一个强指针引用该对象,对象就不会被释放。
注:1. ARC不能使用release等MRC的方法,而使用strong/weak等自己的方法。
2. ARC下创建的对象默认是强指针,当一个对象如果没有强指针即使有若指针也会被释放。
补充:MRC转ARC(Xcode菜单栏 Edit -> convert -> to OC ARC)
问:MRC下给成员变量赋值使用 set方法 还是下划线访问成员变量赋值?
答:使用set方法即点语法进行赋值
因为点语法进行赋值在MRC下会重写以下set方法进行内存管理,而下划线赋值则仅仅是简单的赋值,可能会造成内存泄漏。@property (noatomic,retain) id obj; -(void)setObj:(id)obj // 1. 快速创建一个对象 { if(_obj! = obj){ // 判断新传入对象是不是原来的对象,避免原来的对象被释放还去访问 [_obj release]; // 释放原来的对象 _obj = [obj retain]; // retain新的对象 } return _obj; }
-
单例在MRC和ARC下
单例:在程序运行期间,保证一个类无论创建多少次,都只会产生一个实例对象。
在 ARC 下,单例的代码包含四部分+(instancetype)shareInstance // 1. 快速创建一个对象 { Person *instance = [[self alloc]init]; return instance; } static Person *_instance = nil; +(instancetype)allocWithZone:(struct _NSZone *)zone //2. alloc创建一个对象 { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // 考虑多线程情况 _instance = [[super allocWithZone:zone]init]; //使用super防止循环引用 }); return _instance; } -(id)copyWithZone:(NSZone *)zone //3. copy创建一个对象 { return _instance; } -(id)mutableCopyWithZone:(NSZone*)zone // 4. mutableCopy创建一个对象 { return _instance; }
在 MRC 下,单例的代码除了ARC的四部分外,增加retain / release / retainCount 三部分
// 1.retain
-(instancetype)retain
{
return _insatance;
}
// 2.release
-(oneway void)release
{
// 什么不做,只要保证实例对象只被创建一次
}
// 3.retainCount
{
return MAXFLOAT;
}