iOS另類的內(nèi)存管理
OS的內(nèi)存管理算是老生常談的問題了,我們寫iOS的時候無時無刻不在涉及到內(nèi)存管理。從開始的MRR(manual retain-release)到后來ARC(Automatic Reference Counting),包括CoreFoundation的內(nèi)存管理都遵守引用計數(shù)的基本原則。
基本的內(nèi)存管理大家肯定都很熟悉,在這里主要說一點,其余的就不多說了。官方文檔有這樣的一段話
- You own any object you create
You create an object using a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy” (for example, alloc, newObject, or mutableCopy).
大意就是說,如果你使用alloc/new/copy/mutableCopy這些開頭的方法創(chuàng)建了一個對象,那么你就會擁有這個對象(retain)。當(dāng)你不用的時候,你就需要手動的去release一次。
舉一個例子,假設(shè)有一個方法,[STObject newObject]
我們應(yīng)該這么使用,如果我們***不release,就會導(dǎo)致Object被泄漏。
- STObject *object = [STObject newObject];
- // do something
- [object release];
既然這樣的話,我們也可以想象出如果我們要自己實現(xiàn)new開頭的方法,我們需要如下代碼
- - (instancetype)newObject {
- return [[[self class] alloc] init];
- }
- + (UIButton *)copyButton {
- return [[UIButton buttonWithType:UIButtonTypeCustom] retain];
- }
那么就產(chǎn)生了以下幾個問題:
MRR下實現(xiàn)了一個newObject方法,該方法遵守約定的原則,返回值會retain+1,然后在ARC下調(diào)用該方法創(chuàng)建對象
MRR下實現(xiàn)了一個newObject方法,該方法沒有遵守約定原則,返回autorelease的對象,然后在ARC下調(diào)用該方法創(chuàng)建對象
ARC下實現(xiàn)了一個newObject方法,然后在MRR下調(diào)用newObject方法創(chuàng)建對象,使用完成之后release
ARC下實現(xiàn)了一個newObject方法,然后在MRR下調(diào)用newObject方法創(chuàng)建對象,使用完成之后沒有release
我們可以自己編寫以上的實驗代碼,然后測試。
最終測試結(jié)果如下:
場景1,3下運行正常
場景2下會crash
場景4下產(chǎn)生內(nèi)存泄露
為什么場景2會crash呢?這是由于ARC下我們編譯器如果看到你是以alloc/new/copy/mutableCopy等開頭的方法創(chuàng)建了對象,則會在使用的***插入一次release操作,由于返回的是autorelease的對象,又被release了一次,所以導(dǎo)致野指針。
場景4產(chǎn)生泄漏的原因也是一樣,ARC下編譯器發(fā)現(xiàn)該方法是new等開頭的時候,方法結(jié)束的時候不會插入release語句,場景4使用的過程中,沒有對newObject進行release,所以會產(chǎn)生泄漏。
如果我們僅使用MRR或者ARC的話,這種問題一般不會出現(xiàn)。這種問題的出現(xiàn)一般是當(dāng)ARC/MRR混編的時候,由于一些編寫的不規(guī)范導(dǎo)致的,所以在寫代碼的過程中,遵守規(guī)范是很有必要的。
如果我們自己編寫alloc/new/copy/mutableCopy開頭的方法的時候,MRR下一定不要忘了返回retain的對象,同樣當(dāng)我們使用alloc/new/copy/mutableCopy的方法創(chuàng)建對象的時候,也不能忘了在用完之后release。
如果我們有一段MRR的代碼,提供了一個new開頭的方法但是沒有遵守規(guī)范,我們ARC下該怎么辦呢?按照上面的結(jié)論,我們正常使用肯定會導(dǎo)致野指針的
在這里呢,如果能改代碼當(dāng)然把代碼都改成遵守規(guī)范的***,如果不能改源碼的話,我們只能修改使用方。在這里提供一種方法:
- SEL selector = NSSelectorFromString(@"copyObject");
- STObject *object = (STObject *)[STObject performSelector:selector];
大家可以嘗試一下,然后思考一下為什么。
關(guān)于iOS的內(nèi)存管理遠遠不止這些,本文中說的大家實際編碼的過程中也很少遇到,僅僅當(dāng)作知識的補充吧~