手动内存管理的机理你们应该已经很是清楚了,简单来讲,只要遵循如下三点就能够在手动内存管理中避免绝大部分的麻烦:objective-c
若是须要持有一个对象,那么对其发送retain 若是以后再也不使用该对象,那么须要对其发送release(或者autorealse) 每一次对retain,alloc或者new的调用,须要对应一次release或autorealse调用设计模式
初学者可能仅仅只是知道这些规则,可是在实际使用时不免犯错。可是当开发者常用手动引用计数 Manual Referecen Counting(MRC)的话,这些规则将逐渐变为本能。你会发现少一个release
的代码怎么看怎么别扭,从而减小或者杜绝内存管理的错误。能够说MRC的规则很是简单,可是同时也很是容易出错。每每很小的错误就将引发crash或者OOM之类的严重问题。api
在MRC的年代里,为了不不当心忘写release
,Xcode提供了一个很实用的小工具来帮助可能存在的代码问题(Xcode3里默认快捷键Shift+A?不记得了),能够指出潜在的内存泄露或者过多释放。而ARC在此基础上更进一步:ARC是Objective-C编译器的特性,而不是运行时特性或者垃圾回收机制,ARC所作的只不过是在代码编译时为你自动在合适的位置插入release
或autorelease
,就如同以前MRC时你所作的那样。所以,至少在效率上ARC机制是不会比MRC弱的,而由于能够在最合适的地方完成引用计数的维护,以及部分优化,使用ARC甚至能比MRC取得更高的运行效率。app
学习ARC很简单,在MRC时代你须要本身retain
一个想要保持的对象,而如今不须要了。如今惟一要作的是用一个指针指向这个对象,只要指针没有被置空,对象就会一直保持在堆上。当将指针指向新值时,原来的对象会被release
一次。这对实例变量,synthesize的变量或者局部变量都是适用的。好比函数
NSString *firstName = self.textField.text;
firstName
如今指向NSString对象,这时这个对象(textField
的内容字符串)将被hold住。好比用字符串@“OneV"做为例子(虽然实际上不该该用字符串举例子,由于字符串的retainCount规则其实和普通的对象不同,你们就把它看成一个普通的对象来看吧…),这个时候firstName
持有了@"OneV"。工具
固然,一个对象能够拥有不止一个的持有者(这个相似MRC中的retainCount>1的状况)。在这个例子中显然self.textField.text
也是@“OneV",那么如今有两个指针指向对象@"OneV”(被持有两次,retainCount=2,其实对NSString对象说retainCount是有问题的,不过anyway~就这个意思而已.)。学习
过了一下子,也许用户在textField
里输入了其余的东西,那么self.textField.text
指针显然如今指向了别的字符串,好比@“onevcat",可是这时候原来的对象已然是存在的,由于还有一个指针firstName
持有它。如今指针的指向关系是这样的:优化
只有当firstName
也被设定了新的值,或者是超出了做用范围的空间(好比它是局部变量可是这个方法执行完了或者它是实例变量可是这个实例被销毁了),那么此时firstName
也再也不持有@“OneV",此时再也不有指针指向@"OneV",在ARC下这种情况发生后对象@"OneV"即被销毁,内存释放。atom
相似于firstName
和self.textField.text
这样的指针使用关键字strong
进行标志,它意味着只要该指针指向某个对象,那么这个对象就不会被销毁。反过来讲,ARC的一个基本规则便是,只要某个对象被任一strong
指针指向,那么它将不会被销毁。若是对象没有被任何strong指针指向,那么就将被销毁。在默认状况下,全部的实例变量和局部变量都是strong
类型的。能够说strong
类型的指针在行为上和MRC时代retain
的property是比较类似的。设计
既然有strong
,那确定有weak
咯~weak
类型的指针也能够指向对象,可是并不会持有该对象。好比:
__weak NSString *weakName = self.textField.text
获得的指向关系是:
这里声明了一个weak
的指针weakName
,它并不持有@“onevcat"。若是self.textField.text
的内容发生改变的话,根据以前提到的"只要某个对象被任一strong指针指向,那么它将不会被销毁。若是对象没有被任何strong指针指向,那么就将被销毁”原则,此时指向@“onevcat"的指针中没有strong
类型的指针,@"onevcat"将被销毁。同时,在ARC机制做用下,全部指向这个对象的weak
指针将被置为nil
。这个特性至关有用,相信无数的开发者都曾经被指针指向已释放对象所形成的EXCBADACCESS困扰过,使用ARC之后,不管是strong
仍是weak
类型的指针,都再也不会指向一个dealloced的对象,从根源上解决了意外释放致使的crash。
不过在大部分状况下,weak
类型的指针可能并不会很经常使用。比较常见的用法是在两个对象间存在包含关系时:对象1有一个strong
指针指向对象2,并持有它,而对象2中只有一个weak
指针指回对象1,从而避免了循环持有。一个常见的例子就是oc中常见的delegate设计模式,viewController中有一个strong
指针指向它所负责管理的UITableView,而UITableView中的dataSource
和delegate
指针都是指向viewController的weak
指针。能够说,weak
指针的行为和MRC时代的assign
有一些类似点,可是考虑到weak
指针更聪明些(会自动指向nil),所以仍是有所不一样的。细节的东西咱们稍后再说。
注意相似下面的代码彷佛是没有什么意义的:
__weak NSString *str = [[NSString alloc] initWithFormat:…]; NSLog(@"%@",str); //输出是"(null)"
因为str
是weak
,它不会持有alloc出来的NSString
对象,所以这个对象因为没有有效的strong
指针指向,因此在生成的同时就被销毁了。若是咱们在Xcode中写了上面的代码,咱们应该会获得一个警告,由于不管什么时候这种状况彷佛都是不太可能出现的。你能够把weak换成strong来消除警告,或者直接前面什么都不写,由于ARC中默认的指针类型就是strong
。
property也能够用strong
或weak
来标记,简单地把原来写retain
和assign
的地方替换成strong
或者weak
就能够了。
@property (nonatomic, strong) NSString *firstName; @property (nonatomic, weak) id delegate;
ARC能够为开发者节省不少代码,使用ARC之后不再须要关心何时retain
,何时release
,可是这并不意味你能够不思考内存管理,你可能须要常常性地问本身这个问题:谁持有这个对象?
好比下面的代码,假设array
是一个NSMutableArray
而且里面至少有一个对象:
id obj = [array objectAtIndex:0]; [array removeObjectAtIndex:0]; NSLog(@"%@",obj);
在MRC时代这几行代码应该就挂掉了,由于array
中0号对象被remove之后就被当即销毁了,所以obj指向了一个dealloced的对象,所以在NSLog的时候将出现EXCBADACCESS。而在ARC中因为obj是strong
的,所以它持有了array
中的首个对象,array
再也不是该对象的惟一持有者。即便咱们从array
中将obj移除了,它也依然被别的指针持有,所以不会被销毁。
ARC也有一些缺点,对于初学者来讲,可能仅只能将ARC用在objective-c对象上(也即继承自NSObject的对象),可是若是涉及到较为底层的东西,好比Core Foundation中的malloc()或者free()等,ARC就鞭长莫及了,这时候仍是须要本身手动进行内存管理。在以后咱们会看到一些这方面的例子。另外为了确保ARC能正确的工做,有些语法规则也会由于ARC而变得稍微严格一些。
ARC确实能够在适当的地方为代码添加retain
或者release
,可是这并不意味着你能够彻底忘记内存管理,由于你必须在合适的地方把strong
指针手动设置到nil,不然app极可能会oom。简单说仍是那句话,你必须时刻清醒谁持有了哪些对象,而这些持有者在何时应该变为指向nil
。
ARC必然是Objective-C以及Apple开发的趋势,从此也会有愈来愈多的项目采用ARC(甚至不排除MRC在将来某个版本被弃用的可能),Apple也一直鼓励开发者开始使用ARC,由于它确实能够简化代码并加强其稳定性。能够这么说,使用ARC以后,因为内存问题形成的crash基本就是过去式了(OOM除外 :P)
咱们正处于由MRC向ARC转变的节点上,所以可能有时候咱们须要在ARC和MRC的代码间来回切换和适配。Apple也想到了这一点,所以为开发这提供了一些ARC和非ARC代码混编的机制,这些也将在以后的例子中列出。另外ARC甚至能够用在C++的代码中,而经过遵照一些代码规则,iOS 4里也可使用ARC(虽然我我的认为在如今iOS 6都呼之欲出的年代已经基本没有须要为iOS 4作适配的必要了)、
总之,聪明的开发者总会尝试尽量的自动化流程,已减轻本身的工做负担,而ARC偏偏就为咱们提供了这样的好处:自动帮咱们完成了不少之前须要手动完成的工做,所以对我来讲,转向ARC是一件不须要考虑的事情。
ARC中关于对象的引用参照,主要有下面几关键字。使用strong, weak, autoreleasing限定的变量会被隐式初始化为nil。
变量声明缺省都带有__strong关键字,若是变量什么关键字都不写,那么缺省就是强参照。
上面已经看到了,这是弱参照的关键字。该概念是新特性,从 iOS 5/ Mac OS X 10.7 开始导入。因为该类型不影响对象的生命周期,因此若是对象以前就没有持有者,那么会出现刚建立就被破弃的问题,好比下面的代码。
若是编译设定OS版本 Deployment Target 设定为这比这低的版本,那么编译时将报错(The current deployment target does not support automated __weak references),这个时候,咱们可使用下面的 __unsafe_unretained。
弱参照还有一个特征,即当参数对象失去全部者以后,变量会被自动付上nil (Zeroing)。
该关键字与__weak同样,也是弱参照,与__weak的区别只是是否执行nil赋值(Zeroing)。可是这样,须要注意变量所指的对象已经被破弃了,地址还还存在,但内存中对象已经没有了。若是仍是访问该对象,将引发「BAD_ACCESS」错误。
该关键字使对像延迟释放。好比你想传一个未初始化的对像引用到一个方法当中,在此方法中实例化此对像,那么这种状况可使用__autoreleasing。他被常常用于函数有值参数返回时的处理,好比下面的例子。
又如函数的返回值是在函数中申请的,那么但愿释放是在调用端时,每每有下面的代码。
即当方法的参数是id*,且但愿方法返回时对象被autoreleased,那么使用该关键字。
补充一个 循环应用问题
由于循环引用而产生的内存泄露也是Instrument没法发现的,因此要特别当心。