前言
之前的兩篇拙文C語言-內(nèi)存管理基礎(chǔ)、C語言-內(nèi)存管理深入 介紹了關(guān)于C語言在內(nèi)存管理方面的相關(guān)知識(shí)。但是對于從事iOS開發(fā)的同胞們來說,顯然Objective-C用的更多,所以筆者想用兩篇文章盡量完整的介紹一下Objective-C的內(nèi)存管理,本文為第一部分,將從類和對象、所有權(quán)策略及引用計(jì)數(shù)機(jī)制、內(nèi)存管理原則、內(nèi)存管理方式等幾個(gè)方面展開。如果你能賞臉閱讀此文,你會(huì)發(fā)現(xiàn)本文利用近一半的篇幅介紹ObjC對象的相關(guān)知識(shí),這是因?yàn)镺bjective-C內(nèi)存管理-管理的是繼承自NSObject的對象的內(nèi)存。閱讀本文要求讀者對C語言內(nèi)存管理有一定的了解,尚不熟悉的同學(xué)請移步到這里:C語言-內(nèi)存管理基礎(chǔ)。話不多說,先附上本文內(nèi)容的思維導(dǎo)圖。

類的結(jié)構(gòu)與加載過程
Objective-C作為一門擴(kuò)充C的面向?qū)ο缶幊陶Z言,其和C語言的區(qū)別之一在于引入“面向?qū)ο蟆彼枷?,能夠靈活的使用類和對象進(jìn)行編程。因此了解類的結(jié)構(gòu)和本質(zhì)對于學(xué)習(xí)Objective-C是非常重要的。
-
類的結(jié)構(gòu)
Objective-C中的類是本身也是一個(gè)Class類型的對象,簡稱類對象。而Class實(shí)際上是一個(gè)指向objc_class結(jié)構(gòu)體的指針。
typedef struct objc_class *Class;
通過查看 objc/runtime.h文件,得知objc_class結(jié)構(gòu)組成,其中包括了一個(gè)類很多信息。
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 父類
const char *name OBJC2_UNAVAILABLE; // 類名
long version OBJC2_UNAVAILABLE; // 類的版本信息,默認(rèn)為0
long info OBJC2_UNAVAILABLE; // 類信息,供運(yùn)行期使用的一些位標(biāo)識(shí)
long instance_size OBJC2_UNAVAILABLE; // 該類的實(shí)例變量大小
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 該類的成員變量鏈表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定義的鏈表
struct objc_cache *cache OBJC2_UNAVAILABLE; // 方法緩存
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 協(xié)議鏈表
#endif
} OBJC2_UNAVAILABLE;

- 獲取內(nèi)存中的類對象
- 利用類的
class方法
【函數(shù)原型】+ (Class)class
【方法說明】類方法,通過特定的一個(gè)類調(diào)用該方法返回一個(gè)Class類型的類對象。
Class classP = [Person class]; //classP現(xiàn)在一個(gè)`Class `類型的類對象
- 利用類的實(shí)例對象的
class方法
【函數(shù)原型】- (Class)class
【方法說明】對象方法,通過某個(gè)類的實(shí)例對象調(diào)用該方法返回一個(gè)Class類型的類對象。
Person *objectP = [[Person alloc] init];
Class classP1 = [objectP class];
可以利用objectP類對象創(chuàng)建Person類的實(shí)例對象,并通過打印確定classP是Person類型。
Person *objectP1 = [[classP new] init];
NSLog(@"%@", NSStringFromClass(classP));
控制臺(tái)輸出:
//2017-02-05 15:49:57.916948 類和對象[4473:131075] Person
打印classP、classP 1內(nèi)存地址發(fā)現(xiàn)數(shù)值相同,說明類在內(nèi)存中只有一份。
NSLog(@"%p, %p",classP, classP1);
// 2017-02-05 15:28:53.340127 類和對象[4238:123830] 0x1000011e0, 0x1000011e0
- 類的加載和初始化
-
類的加載
【函數(shù)原型】+ (void)load
【函數(shù)說明】 程序運(yùn)行把Xcode的"Compile Sources"選項(xiàng)中存在的類和分類(不管有沒有用到)加載進(jìn)來時(shí)調(diào)用,先調(diào)用父類再調(diào)用子類,每個(gè)類調(diào)用一次。 -
類的初始化
【函數(shù)原型】+ (void)initialize
【函數(shù)說明】第一次使用到該類時(shí)調(diào)用。優(yōu)先調(diào)用該類分類中的initialize方法,子類調(diào)用時(shí)會(huì)先調(diào)用父類的initialize方法初始化父類。
【區(qū)別】:load方法類和分類都會(huì)調(diào)用,因?yàn)樗麄兪欠珠_加載的,分類的加載順序和編譯順序有關(guān);initialize方法是首次使用到該類時(shí)調(diào)用,調(diào)用順序是先調(diào)用該類分類中initialize,再調(diào)用該類父類分類的initialize;如果沒有會(huì)調(diào)用該類的initialize,再調(diào)用父類的initialize。
對象的創(chuàng)建
Objective-C中創(chuàng)建對象,先調(diào)用類的alloc方法返回對象再調(diào)用對象的init 或initWithSomething方法返回自己亦或是直接調(diào)用該類的new方法。例如下面代碼:
Person *p1 = [[Person alloc] init];
Person *p2 = [Person new];
依次介紹下面三個(gè)方法:
-
+ (instancetype) alloc
該方法被調(diào)用時(shí)系統(tǒng)首先在堆區(qū)分配合適大小的內(nèi)存存儲(chǔ)該對象(參考C語言-內(nèi)存管理基礎(chǔ)-C語言操作堆內(nèi)存的函數(shù)部分內(nèi)容),并返回一個(gè)未被初始化的該類型的對象,并完成下面三件事情:(引自談ObjC兩段構(gòu)造模式)
1,將該新對象的引用計(jì)數(shù) (Retain Count) 設(shè)置成 1。
2,將該新對象的 isa 成員變量指向它的類對象。
3,將該新對象的所有其它成員變量的值設(shè)置成零。(根據(jù)成員變量類型,零有可能是指 nil 或 Nil 或 0.0)
- (instancetype) init
根據(jù)對象具體成員變量的類型真正初始化對象的成員變量的值。+ (instancetype) new
可以簡單的理解為將alloc、init合并為一次操作。
-
對象的存儲(chǔ)
上述創(chuàng)建p1、p2的代碼可以解讀為:
- 系統(tǒng)在內(nèi)存的棧區(qū)分別開辟空間存儲(chǔ)
p1、p2指針變量 - 系統(tǒng)在內(nèi)存的堆區(qū)分別開辟空間存儲(chǔ)兩個(gè)
Person對象并使棧區(qū)的p1、p2指針變量分別指向堆區(qū)的Person對象。
對象存儲(chǔ)簡單圖解
-
更多細(xì)節(jié)
關(guān)于ObjC對象的更多細(xì)節(jié)請參考下面的文章:
唐巧:談ObjC兩段構(gòu)造模式
冰霜:Objc 對象的今生今世
Matt Gallagher:What is a meta-class in Objective-C?
上文中有部分描述引自前者,已經(jīng)做出了說明。
對象的所有權(quán)及引用計(jì)數(shù)
所有權(quán)策略:任何自己創(chuàng)建的對象都?xì)w自己所有且都存在一個(gè)或多個(gè)所有者,只要對象至少存在一個(gè)所有者,該對象就不會(huì)被銷毀,其占用的內(nèi)存空間就會(huì)一直存在不被釋放(除非整個(gè)程序已經(jīng)退出)。類似于一瓶礦泉水可能被一個(gè)或多個(gè)人飲用,只要還有一個(gè)人需要飲用該礦泉水,它就不會(huì)被環(huán)衛(wèi)工人回收。
通過NSObject協(xié)議中的retainCount屬性可以獲得該對象當(dāng)前的引用計(jì)數(shù)值。引用計(jì)數(shù)器:Cocoa采用一種引用計(jì)數(shù)機(jī)制,為每個(gè)對象都綁定一個(gè)NSUInteger類型的整數(shù)表示該對象當(dāng)前被引用的次數(shù)(即當(dāng)前共有多少個(gè)所有者引用著該對象),稱之為該對象的引用計(jì)數(shù)器。每個(gè)ObjC對象都有自己的引用計(jì)數(shù)器(在64位編譯器環(huán)境下占用8個(gè)字節(jié)空間)。類似于一個(gè)整數(shù)記錄一瓶礦泉水當(dāng)前被多少個(gè)人飲用,當(dāng)該礦泉水沒有被人飲用時(shí)就要被環(huán)衛(wèi)工人回收再利用了。
引用計(jì)數(shù)器的作用:對象剛被創(chuàng)建時(shí)其引用計(jì)數(shù)默認(rèn)為1,當(dāng)對象的引用計(jì)數(shù)器值變?yōu)?時(shí)(即已經(jīng)沒有所有者引用該對象),系統(tǒng)銷毀該對象,釋放并重新利用該對象在堆區(qū)對應(yīng)分配的存儲(chǔ)空間。系統(tǒng)通過判斷對象的引用計(jì)數(shù)器值是否為0來決定是否需要銷毀該對象并釋放其占用的內(nèi)存空間。
一種例外情況:若對象值為nil時(shí)其引用計(jì)數(shù)為0但系統(tǒng)不回收空間,因?yàn)橄到y(tǒng)尚未為該對象分配空間。
Person *p1 = nil;
NSLog(@"%lu", (unsigned long)p1.retainCount); //p1.retainCount = 0
- 與引用計(jì)數(shù)相關(guān)的操作
-
- (instancetype)retain:使對象的引用計(jì)數(shù)器值+1 -
- (oneway void)release:使對象的引用計(jì)數(shù)器值-1(不代表銷毀該對象) -
- (NSUInteger)retainCount:獲得對象當(dāng)前的引用計(jì)數(shù)器值 -
- (instancetype)autorelease:待稍后清理“自動(dòng)釋放池”(@autoreleasepool)時(shí),再減少對象的引用計(jì)數(shù)
值得注意的是:retain操作無法使一個(gè)已經(jīng)被釋放的對象的引用計(jì)數(shù)器值+1,這也很好理解,retain無法讓一個(gè)已經(jīng)死去的人起死回生,就像環(huán)衛(wèi)工人已經(jīng)回收了你的礦泉水瓶,那么你將不能再持有該水瓶。
UIView *view = [[UIView alloc] init];
NSLog(@"創(chuàng)建之后的默認(rèn)值是:%ld",(unsigned long)view.retainCount); //創(chuàng)建之后的默認(rèn)值是:1
[view retain];
NSLog(@"通過一次retain操作之后:%ld",(unsigned long)view.retainCount); //通過一次retain操作之后:2
[view release];
NSLog(@"通過一次release操作之后:%ld",(unsigned long)view.retainCount); //通過一次release操作之后:1
上面的四種操作中除開`autorelease `方法外都比較好理解,關(guān)于`autorelease `可以點(diǎn)擊[黑幕背后的Autorelease](http://blog.sunnyxx.com/2014/10/15/behind-autorelease/)。同時(shí)《Effective Objective-C 2.0 編寫高質(zhì)量iOS與OS X代碼的52個(gè)有效方法》書中有這樣解釋的:
在OC引用計(jì)數(shù)框架中,自動(dòng)釋放池是一項(xiàng)重要特性,調(diào)用release會(huì)立刻遞減對象的引用計(jì)數(shù)(而且很可能令系統(tǒng)回收此對象)然而有時(shí)候可以不調(diào)用它,改為調(diào)用autorelease,此方法會(huì)在稍后遞減計(jì)數(shù),通常是在下一次“事件循環(huán)“(even loop)時(shí)遞減,不過也可能執(zhí)行的更早。此特性很有用,尤其是在方法中返回對象時(shí)更應(yīng)該用它。
該書中還舉出了這樣的例子:
- (NSString *)stringValue {
NSString *str = [[NSString alloc] initWithFormat:@"i am this: %@",self];
return str;
}
此時(shí)返回的str對象計(jì)數(shù)值比期望值要+1,因?yàn)榉椒▋?nèi)部調(diào)用了一次alloc操作而又沒有與之對應(yīng)的釋放操作,計(jì)數(shù)器+1就意味著調(diào)用者要負(fù)責(zé)處理多出來的這一次保留操作。但是不能在- (NSString *)stringValue方法內(nèi)部釋放,否則還沒等方法返回,系統(tǒng)就把該對象回收了,這時(shí)候應(yīng)該使用autorelease,他能夠延長對象的生命周期,保證方法返回后該對象一定有效并且在合適的時(shí)機(jī)得以釋放。
【注意】:關(guān)于一個(gè)對象的retainCount大部分情況下是合理的,但是有些時(shí)候其數(shù)值是令人無法理解的。例如蘋果官方文檔對其做了下面的解釋:

文檔的大致內(nèi)容是:此方法在調(diào)試內(nèi)存管理問題上作用不大。 因?yàn)槿我獾目蚣軐ο罂赡芤迷搶ο?,同時(shí)自動(dòng)釋放池對對象有延遲釋放作用,因此無法通過調(diào)用此方法獲取到有用的信息。
-
對象的銷毀
- 對象何時(shí)被銷毀:當(dāng)對象的引用計(jì)數(shù)器值為0時(shí),它將被系統(tǒng)銷毀,其占用的內(nèi)存空間將得到釋放。
-
- (void)dealloc:當(dāng)對象被銷毀時(shí),系統(tǒng)會(huì)調(diào)用該方法,重寫該方法并在其中釋放相應(yīng)的資源如移除通知等(類似于臨終遺言),一旦重寫dealloc方法就必須在代碼塊最后調(diào)用[super dealloc]方法,調(diào)用[super dealloc]方法是為了讓super釋放相應(yīng)的資源,使得任何繼承來的對象都能夠得到釋放;另外我們不應(yīng)該直接調(diào)用對象的delloc方法。
例如下面的代碼:
@implementation Person
- (void)dealloc
{
NSLog(@"對象被銷毀了");
[super dealloc];
}
@end
Person *p1 = [[Person alloc] init];
NSLog(@"%lu",(unsigned long)p1.retainCount);
[p1 release];
控制臺(tái)輸出信息
2017-01-28 18:11:29.431764 OC中對象內(nèi)存管理[20948:160747] 1
2017-01-28 18:11:29.432173 OC中對象內(nèi)存管理[20948:160747] 對象被銷毀了
內(nèi)存管理的范圍
有了上面關(guān)于Objc對象知識(shí)的鋪墊,我們可以很好的引入內(nèi)存管理這個(gè)概念。
范圍:管理任何繼承自NSObject類型的對象的內(nèi)存。對于其他基本數(shù)據(jù)類型如:int、char、double、結(jié)構(gòu)體、枚舉類型的變量無效。
根本原因:ObjC對象和其他的基本類型變量在內(nèi)存中存儲(chǔ)位置不同,對象所占的內(nèi)存由系統(tǒng)在堆區(qū)動(dòng)態(tài)分配需要程序員管理手動(dòng)釋放,而其他基本數(shù)據(jù)類型的變量(局部變量)一般分配在棧區(qū)由系統(tǒng)自動(dòng)釋放。
內(nèi)存管理的方式
- Objective-C中為我們提供了兩種(曾經(jīng)是三種)管理內(nèi)存的方式:
Mannul Reference Counting (MRC)
手動(dòng)內(nèi)存管理,指的是通過retain、release、autorelease等使用引用計(jì)數(shù)器操作的方式由程序員手動(dòng)管理內(nèi)存。本文中所有涉及到以上三個(gè)方法的代碼都是MRC的實(shí)踐。MRC最大的問題在于持有和釋放對象的時(shí)機(jī),不當(dāng)?shù)?code>retain和release極可能產(chǎn)生“野指針”和“內(nèi)存泄露”等內(nèi)存方面的問題(下文中有具體介紹)。-
Automatic Reference Counting (ARC)
自動(dòng)內(nèi)存管理,ARC作為WWDC2011和iOS5之后LLVM 3.0編譯器的一項(xiàng)新特性,極大的解放了iOS開發(fā)者的雙手(蘋果推薦使用)。在ARC的編譯環(huán)境下開發(fā)者再也不用寫任何含有retain、release、autorelease的代碼了,編譯器將自動(dòng)在合適的地方插入上述代碼實(shí)現(xiàn)內(nèi)存的管理。事實(shí)上現(xiàn)在絕大部分代碼都是使用的ARC管理內(nèi)存,只是作為初學(xué)者可能“日用而不知”,并沒有意識(shí)到其中深層次的關(guān)系,并且在ARC環(huán)境下編寫含有引用計(jì)數(shù)操作的代碼是無法編譯通過的。例如:
ARC與引用計(jì)數(shù)操作
ARC與 [supper delloc]

關(guān)于更多MAC和ARC機(jī)制的內(nèi)容請參考以下博客:
王巍 (@onevcat):手把手教你ARC——iOS/Mac開發(fā)ARC入門和使用
HIT-Alibaba:Objective-C 中的內(nèi)存分配
Apple:Transitioning to ARC Release Notes
- Gargage Collection (垃圾回收機(jī)制)
Objective-C 2.0以后存在垃圾回收機(jī)制。垃圾回收機(jī)制監(jiān)視整個(gè)對象關(guān)系圖,查找那些在作用域內(nèi)已沒有任何指針指向的對象,并自動(dòng)釋放這些對象。
另外在《Effective Objective-C 2.0 編寫高質(zhì)量iOS與OS X代碼的52個(gè)有效方法》一書中對垃圾回收機(jī)制有這樣的描述:
從Mac OS X10.8開始,“垃圾收集器”(garbage collector)已經(jīng)正式廢棄了,以后Objective-C代碼編寫Mac OS X程序時(shí)不應(yīng)再使用它,而iOS則從未支持過垃圾收集。
由于關(guān)于Objective-C垃圾回收的資料本人收集的比較少,僅在《好學(xué)的Objective-C》一書中看到過相關(guān)介紹,而且該機(jī)制從未在iOS開發(fā)中使用到,所以筆者這里姑且認(rèn)為Objective-C中是存在過三種內(nèi)存管理的方式,只是沒法介紹關(guān)于它的更多內(nèi)容。有興趣的同學(xué)可以查閱一下該書。
內(nèi)存管理的原則
- 只要對象還在被使用其占用的內(nèi)存空間就不應(yīng)該被回收;需要使用該對象,使該對象引用計(jì)數(shù)+1;使用完該對象,使該對象的引用計(jì)數(shù)-1。
- 誰創(chuàng)建,誰release
- 誰retain,誰release
具體來說當(dāng)使用alloc、new、copy(生成一個(gè)接受對象的副本)創(chuàng)建對象時(shí),其引用計(jì)數(shù)器值被置為1,當(dāng)不需要使用該對象時(shí)需要做一次release操作;當(dāng)使用retain操作持有一個(gè)對象時(shí)其引用計(jì)數(shù)器值+1,當(dāng)不需要使用該對象時(shí)需要做一次release操作;一次創(chuàng)建對應(yīng)著一次release,一次retain對應(yīng)著一次release,這樣對象才能“有始有終”。
內(nèi)存管理不當(dāng)導(dǎo)致的問題
- 野指針
- 定義的指針變量沒有初始化。
- 指向的堆內(nèi)存空間已經(jīng)被釋放的指針。
Dog *yellowDog = [[Dog alloc] init];
[yellowDog release];
NSLog(@"%@", yellowDog);
上面的代碼在yellowDog 的引用計(jì)數(shù)器值為0時(shí)已經(jīng)將在堆區(qū)分配的 Dog 對象的內(nèi)存釋放掉了,再調(diào)用NSLog通過yellowDog指針訪問堆區(qū)的對象就很可能會(huì)出問題。
Person *p1 = [Person new];
[p1 release];
[p1 release];
如上的代碼,當(dāng)?shù)谝淮螆?zhí)行[p1 release]代碼時(shí),p1指向的對象的retainCount減為0,系統(tǒng)銷毀該對象且其在堆區(qū)的存儲(chǔ)空間會(huì)被回收,如果此時(shí)再次執(zhí)行[p1 release]代碼,嘗試給已經(jīng)被釋放的Pweson對象發(fā)送release消息,很可能會(huì)觸發(fā)運(yùn)行時(shí)錯(cuò)誤。具體來說程序運(yùn)行成功但是控制臺(tái)會(huì)輸出這樣的信息:
Person object 0x1002049e0 overreleased while already deallocating; break on objc_overrelease_during_dealloc_error to debug
《Effective Objective-C 2.0 編寫高質(zhì)量iOS與OS X代碼的52個(gè)有效方法》一書是這樣解釋 “很可能”這個(gè)詞的:
之所以說“很可能”沒有說一定,是因?yàn)閷ο笤诙褏^(qū)所占的內(nèi)存在“解除分配”(deallocated)之后只是放回到“可用內(nèi)存池”,如果此時(shí)執(zhí)行NSLog時(shí)尚未覆蓋對象內(nèi)存,那么該對象依舊有效,這時(shí)程序不會(huì)奔潰。由此可見,因過早釋放對象而導(dǎo)致的bug很難調(diào)試。
當(dāng)我們開啟Xcode的僵尸對象檢測功能時(shí),程序運(yùn)行崩潰,在控制臺(tái)輸出如下信息:
*** -[Person release]: message sent to deallocated instance 0x100200640
//release 消息發(fā)送給了一個(gè)已經(jīng)釋放掉的對象
上面的情況在日常開發(fā)中常常體現(xiàn)為Thread:EXC_BAD_ACCESS(壞訪問錯(cuò)誤)
【壞訪問錯(cuò)誤】:即訪問了一塊壞內(nèi)存(不可用的已經(jīng)被回收的內(nèi)存)。這樣的錯(cuò)誤我們也稱為“野指針錯(cuò)誤”,意思是利用野指針操作了一塊不可用的內(nèi)存。
【僵尸對象】:所占用的內(nèi)存空間已經(jīng)被回收不可用的對象。僵尸對象不應(yīng)該再使用。
【解決方案】:為避免在不經(jīng)意間使用了無效對象,一般調(diào)完realease之后都會(huì)清空指針。這樣就能保證不會(huì)可能出現(xiàn)指向無效對象的指針。即在對象釋放完畢后將指向?qū)ο蟮闹羔樦脼?code>nil,給nil發(fā)消息不會(huì)產(chǎn)生任何反應(yīng)。
綜上所述:沒有置為nil卻指向了無效對象的指針就是野指針。
所以正確的做法應(yīng)該是:
Dog * yellowDog = [[Dog alloc] init];
[yellowDog release];
yellowDog = nil //對象釋放后,將指向?qū)ο蟮闹羔樦脼閚il
- 內(nèi)存泄漏
{ //代碼塊內(nèi)創(chuàng)建Dog對象
Dog *husky = [[Dog alloc] init];
}
形如上面的代碼,當(dāng)我們在創(chuàng)建Dog對象時(shí)系統(tǒng)分別在棧區(qū)為husky變量分配內(nèi)存、在堆區(qū)為husky所指向的Dog對象分配內(nèi)存,由于變量husky的作用域在大括號(hào)內(nèi)屬于局部變量,當(dāng)代碼塊執(zhí)行完畢,棧中的husky變量就被釋放了,但是此時(shí)在代碼塊中并沒有對husky變量指向的堆區(qū)的對象存儲(chǔ)空間進(jìn)行釋放,所以我們說堆區(qū)中對應(yīng)的存儲(chǔ)空間就被泄露了。
總結(jié)為:基本的數(shù)據(jù)類型由于占用的存儲(chǔ)空間是固定的,一般存在棧區(qū)中。由于棧中主要存放的是局部變量,局部變量占用的內(nèi)存空間是其所在的代碼塊或者是函數(shù)結(jié)束的時(shí)候自動(dòng)回收,指向?qū)ο蟮闹羔樢矔?huì)被回收,這個(gè)過程不需要程序員管理。但是對象創(chuàng)建完成后是存放在堆區(qū)的,由于此時(shí)指向?qū)ο蟮闹羔樢呀?jīng)被回收,但是對象仍然存在內(nèi)存中,就會(huì)造成內(nèi)存泄漏(申請的空間已經(jīng)不再使用卻沒有被及時(shí)合理的釋放掉)。
文章最后
以上就是筆者對于Objective-C內(nèi)存管理基礎(chǔ)認(rèn)識(shí)的全部內(nèi)容,部分描述引自書籍和其它博客文中已經(jīng)做出了說明,但凡是本人認(rèn)為重要的概念均附上其它博客的鏈接用于擴(kuò)展相關(guān)知識(shí)。
另外:本文涉及到MRC的代碼需要在Xcode進(jìn)行如下設(shè)置才能編譯通過:在Build Settings中,找到"Objective-C Automatic Reference Counting"這個(gè)選項(xiàng),將它的值改為"NO"。
如果文中有任何紕漏或錯(cuò)誤歡迎在評論區(qū)留言指出,本人將在第一時(shí)間修改過來;喜歡我的文章,可以關(guān)注我以此促進(jìn)交流學(xué)習(xí); 如果覺得此文戳中了你的G點(diǎn)請隨手點(diǎn)贊;轉(zhuǎn)載請注明出處,謝謝支持。


