前言
大家好,我是milo,前面对那么多关于MRC的知识做了总结,今天终于过渡到ARC来了,其实学习就是深入浅出,懂得更多基础,能够让你在开发中都做到心中有数,不至于盲目,共勉。
ARC简介
ARC是ios5.0之后增加的新特性,全称Automatic Reference Counting(自动引用计数),不同于MRC手动管理内存的方式,ARC会让编译器会自动在适当的地方插入适当的retain、release、autorelease语句,也就是说不需要程序猿手动进行内存管理了。而且我们创建文件的时候默认都是使用ARC的。
ARC的使用演示
什么使用?根本就不需要去想它在哪好吗?它是编译器的特性,我们写代码时它会自动暗中帮我们补全的,请看下面代码示例:
JJPerson.m里重写一下dealloc方法
(注意:可重写,不可[super dealloc],ARC的管理方式下,oc对象不能写关于内存的代码)
main.m里写一个对象不写内存管理的代码
int main(int argc, const char * argv[]) {
JJPerson *person = [[JJPerson alloc] init];
JJDog *dog = [[JJDog alloc] init];
[person setDog:dog];
return 0;
}
打印结果:
2018-06-26 15:09:09.007235+0800 ARC[1353:78888] -[JJPerson dealloc]
2018-06-26 15:09:09.007475+0800 ARC[1353:78888] -[JJDog dealloc]
Program ended with exit code: 0
所以在ARC下,我们不写内存管理的代码,不会引起内存问题(而且ARC也不会让你写的)。
ARC的判断原则
只要还有一个强指针变量指向对象,对象就会保持在内存中
强指针:默认所有的指针都是强指针。只要是有强指针指向一个对象,那么这个对象就不会被释放,如果没有强指针指向一个对象,那么这个对象就被立即回收,用__strong标示,但是因为默认所有指针都是强指针,所以他没有用。
弱指针:弱指针指向的对象不影响对象回收,用__weak标示,ARC中也存在循环引用的问题,所以弱指针常用在循环引用,当出现循环引用的时候,必须要有一端是弱指针。
强指针:
int main(int argc, const char * argv[]) {
__strong JJPerson *person = [[JJPerson alloc] init];
// __strong写不写都是默认__strong的,所以没有用;但__weak就不能乱用了,如果在这里代替__strong,那么刚创建的对象马上就被销毁
JJDog *dog = [[JJDog alloc] init];
[person setDog:dog];
return 0;
}
强指针的使用比较简单,默认不写就行了;而弱指针则要注意以下两点。
弱指针:
1、出现循环依赖的时候要让一端为weak,这一点和MRC一样,只不过MRC使用的是assign
2、刚创建的对象不能使用weak指针指向它,否则会立即释放,因为没有强指针指向它。比如我们常常会使用weak的方式创建控件,但是我们不能直接让weak指针直接指向对象,而是应该先直接创建出控件,再让指针指向它
ARC的使用细节
ARC中的@property
strong : 用于OC对象, 相当于MRC中的retain
weak : 用于OC对象, 相当于MRC中的assign
assign : 用于基本数据类型, 跟MRC中的assign一样
copy : 一般用于NSString, 跟MRC中的copy一样
不能调用release、retain、autorelease、retainCount
可以重写dealloc,但是不能调用[super dealloc]
ARC的优点
ARC是编译器特性,而不是运行时特性,ARC不是其它语言中的垃圾回收, 有着本质区别。
1、完全消除了手动管理内存的烦琐, 让程序猿更加专注于app的业务
2、基本上能够避免内存泄露
3、有时还能更加快速,因为编译器还可以执行某些优化
----------------------------深究部分-------------------------------
看完前面我们就对ARC有了初步的认识了,它就是编译器偷偷摸摸并且合理地把你的对象内存释放掉了,这个合理的时机就是:
结论:通过alloc、new、copy等方式创建出来的对象是在出了作用域后被销毁;而通过get方法、类方法等非前面所说的方式创建出来的对象都是在出了autoreleasepool后被释放的。
(都要满足没有其他强指针指向对象)
跟MRC很像对吧!所以说ARC就是帮我们做了MRC要做的工作并且进行了优化。
下面代码通过打断点+重写dealloc方法就可以让我们知道对象的释放时间点
int main(int argc, const char * argv[]) {
// {}表示一个作用域
@autoreleasepool{
{
JJPerson *person = [[JJPerson alloc] init];
}// 在这行person被释放
}
@autoreleasepool{
{
JJPerson *newPerson = [JJPerson person];
}
}// 在这行newPerson被释放
return 0;
}
但是作用域归作用域(包括autoreleasepool),它也满足没有强指针指向对象就释放的原则,如果依旧还是有一个强指针指向这个对象,那就算出了作用域也无法被释放,作用域的作用可以理解为对里面的指针做一次release操作
见下面代码:
int main(int argc, const char * argv[]) {
// {}表示一个作用域
@autoreleasepool{
JJPerson *person1; // 定义一个在作用域外的指针
{
JJPerson *person = [[JJPerson alloc] init];
person1 = person;// 让外界指针指向对象
}// 这行对象没有释放,因为还有一个外界指针指向它
}// 在这行对象被释放,因为外界指针出autoreleasepool这个作用域后被释放
@autoreleasepool{
{
JJPerson *newPerson = [JJPerson person];
}
}// 在这行newPerson被释放
return 0;
}
深究以后我们就能发现,ARC的内存释放和MRC的内存释放基本都是一样的喔!该用release和autorelease的时机跟我前面分析的MRC都一样的喔!很有意思吧!