Objective-C 的運(yùn)行時(shí)以及 Swift 的動(dòng)態(tài)性

自 Swift 推出以來,人們總是不斷希望 Swift 能夠「更加動(dòng)態(tài)化」。但是「動(dòng)態(tài)化 (dynamic)」又是什么意思呢?為什么 Objective-C 比 Swift 更加動(dòng)態(tài)化呢?如果我們無法使用動(dòng)態(tài)化的語言,那么該如何去構(gòu)建以往那些依賴于動(dòng)態(tài)性的東西呢?在本次Mobilization 2016大會(huì)上,Roy Marmelstein 詳細(xì)闡述了這些問題。

概述(0:00)

今天我講演的題目是「Objective-C 的運(yùn)行時(shí)以及 Swift 的動(dòng)態(tài)性」。這里我添加了一條「2016 年的觀點(diǎn)」的限定性標(biāo)注,因?yàn)槲抑v的很多內(nèi)容都將在未來幾年內(nèi)發(fā)生改變。

讓我們回到五個(gè)月前,大概是五月中旬吧,陽光明媚,春暖花開,著名的 Objective-C 和 iOS 開發(fā)者——Brent Simmons 發(fā)表了一系列博文。在這些博文當(dāng)中,他記錄了各種開發(fā)者們借助 Objective-C 的運(yùn)行時(shí)機(jī)制而解決的問題。他試圖證明的是,Swift 作為一門編程語言,竟然沒有這些問題的原生解決方案。即便時(shí)至今日,Swift 仍然捆綁了 Objective-C 的運(yùn)行時(shí)機(jī)制,就算未來 Swift 完全將 Objective-C 取而代之,我們?nèi)匀徊恢涝撊绾谓鉀Q這些問題。

接下來,便是 Twitter 上的一場宏大的戰(zhàn)役,人們開始站隊(duì)。過去 20 年來一直在為 Mac 開發(fā) Objective-C 應(yīng)用的人們被視為保守派,而 Swift 開發(fā)人員則被視為改革派。一方代表了經(jīng)驗(yàn)和靈活性,而另一方則代表了理想化和類型安全。網(wǎng)上充斥著各種各樣針鋒相對的推文和 Medium 文章,最終事態(tài)變得無法控制,甚至為此你都能夠買相關(guān) T-shirt 來聲援自己的陣營。

因此,我覺得是時(shí)候應(yīng)該來談?wù)撘幌逻@個(gè)敏感的話題了。今日,我們將一起來探索運(yùn)行時(shí)函數(shù),并且談?wù)撘幌滤^的「動(dòng)態(tài)性」到底是什么意思。我們將看一下 Swift 如今所處的位置,探究其缺陷,并展望未來。這些內(nèi)容可能會(huì)有些紛繁復(fù)雜,因此我保證最后會(huì)放上一只很酷的貓咪 GIF 圖作為結(jié)束。是不是很值得期待呢?

Objective-C 運(yùn)行時(shí)(2:06)

在我們開始之前,需要強(qiáng)調(diào)一點(diǎn),Objective-C 是一門基于運(yùn)行時(shí)的編程語言,這意味著所有方法、變量、類之間的鏈接,都會(huì)推遲到應(yīng)用實(shí)際運(yùn)行的最后一刻才會(huì)建立。這將給開發(fā)人員極高的靈活性,因?yàn)槲覀兛梢孕薷倪@些鏈接。而不同的是,Swift 絕大多數(shù)時(shí)候是一門面向編譯時(shí)的語言。因此在 Swift 當(dāng)中,靈活性受到了限制,不過您會(huì)因此得到更多的安全性。

這就是本次辯論的核心內(nèi)容。所以不用多說,讓我們開始吧!

Objective-C 的運(yùn)行時(shí)本質(zhì)上是一個(gè)庫。它負(fù)責(zé)了 “Objective” 這個(gè)部分,因此您所知、所愛的面向?qū)ο缶幊蹋际窃谶@里實(shí)現(xiàn)的。如果您想要訪問里面的函數(shù)的話,只需要導(dǎo)入這個(gè)庫即可:

#import

它主要由 C 和匯編編寫而成,其實(shí)現(xiàn)了諸如類、對象、方法調(diào)度、協(xié)議等等這些東西。它是完全開源的,并且開源了很長一段時(shí)間了。您可以將源碼下載下來,查看一下面向?qū)ο蟮倪@些特性是如何實(shí)現(xiàn)的,從而更深、更好地掌握我們用于開發(fā)的這門語言。

運(yùn)行時(shí)負(fù)責(zé) Objective-C 中的面向?qū)ο缶幊踢@個(gè)部分。讓我們從基本的構(gòu)建模塊開始。那么什么是對象呢?對象在runtime.h當(dāng)中是這樣定義的:

typedefstructobjc_class*Class;structobjc_object{Classisa;};

對象只與一個(gè)類建立引用關(guān)聯(lián),也就是這個(gè)isa的意思所在。這也就是 Objective-C 當(dāng)中的所有對象都需要實(shí)現(xiàn)的。那么類又是什么呢?類的定義要稍微復(fù)雜一些。

structobjc_class{Classisa;Classsuper_class;constchar*name;longversion;longinfo;longinstance_size;structobjc_ivar_list*ivars;structobjc_method_list**methodLists;structobjc_cache*cache;structobjc_protocol_list*protocols;};

類當(dāng)中同樣有isa這個(gè)值。它與super_class這個(gè)值進(jìn)行關(guān)聯(lián)。除了NSObject這個(gè)類之外,super_class的值永遠(yuǎn)不會(huì)為nil,因?yàn)?Objective-C 當(dāng)中的其余類都是以某種方式繼承自NSObject的。之后,我們還有name、version、info之類的值,不過這些并不是我們感興趣的內(nèi)容。

對于我們而言,更多的應(yīng)該是關(guān)注變量列表 (ivars)、方法列表 (methodLists) 和這個(gè)協(xié)議列表 (protocols)。這些就是我們能在運(yùn)行時(shí)修改和讀取的??梢钥吹剑瑢ο笃鋵?shí)本質(zhì)上是一個(gè)非常簡單的結(jié)構(gòu)體,類同樣也是。我們可以借助運(yùn)行時(shí)函數(shù),從而在運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建類。

我們?yōu)槭裁匆@么做呢?因?yàn)檫@個(gè)函數(shù)被大量運(yùn)用在庫提供者制作的框架當(dāng)中。如果您無法知道用戶將會(huì)創(chuàng)建什么樣的數(shù)據(jù),那么您就需要在運(yùn)行時(shí)進(jìn)行類的創(chuàng)建了。Core Data 就使用了這個(gè)功能。此外,如果您愿意的話,它還可以用在 JSON 解析當(dāng)中。

ClassmyClass=objc_allocateClassPair([NSObjectclass],"MyClass",0);// 在這里添加變量、方法和協(xié)議objc_registerClassPair(myClass);// 當(dāng)類注冊之后,變量列表將會(huì)被鎖定[[myClassalloc]init];

這就是我們要用的 Objective-C 運(yùn)行時(shí)函數(shù):allocateClassPair。我們?yōu)槠涮峁┮粋€(gè)isa,在本例當(dāng)中我們提供了NSObject,然后為其命名。第三個(gè)參數(shù)則是額外字節(jié)的定義,通常我們都直接賦值 0 即可。隨后我們就可以添加變量、方法以及協(xié)議了,之后就注冊這個(gè)ClassPair。注冊之后,我們就無法修改變量列表了,不過其余的內(nèi)容仍然可以修改。

結(jié)束~我們所創(chuàng)建的這個(gè)類和其余的 Objective-C 類毫無區(qū)別。

類別(5:34)

如果您想要擴(kuò)展一個(gè)不是自己創(chuàng)建的類,想要向其中添加函數(shù),有一個(gè)便捷的方法便是使用 Objective-C 的類別 (Category) 特性。Swift 的擴(kuò)展與之非常相似。類別的一個(gè)問題便在于,它無法添加存儲屬性。您可以添加一個(gè)計(jì)算屬性,但是存儲屬性是無法添加的。

運(yùn)行時(shí)的另一個(gè)特性便是:我們可以借助setAssociatedObject和getAssociatedObject這兩個(gè)函數(shù),向既有的類當(dāng)中添加存儲屬性。

@implementationNSObject(AssociatedObject)@dynamicassociatedObject;-(void)setAssociatedObject:(id)object{objc_setAssociatedObject(self,@selector(associatedObject),object,OBJC_ASSOCIATION_RETAIN_NONATOMIC);}-(id)associatedObject{returnobjc_getAssociatedObject(self,@selector(associatedObject));}

對于不是自己創(chuàng)建的類而言,使用這個(gè)方法進(jìn)行擴(kuò)展無疑是非常好用的。

接下來我們要介紹的,便是判別這個(gè)類能執(zhí)行何種操作。這就是所謂的「內(nèi)省 (introspection)」機(jī)制。通常,我們所使用的往往是最基礎(chǔ)的內(nèi)省功能。

[myObjectisMemberOfClass:NSObject.class];[myObjectrespondsToSelector:@selector(doStuff:)];// isa == classclass_respondsToSelector(myObject.class,@selector(doStuff:));

首先是這個(gè)isMemberOfClass,這是 Foundation 當(dāng)中的一部分,這里我們查看myObject是否是NSObject的子類。接下來是這個(gè)respondsToSelector:,當(dāng)我們使用了一個(gè)帶有可選方法的協(xié)議時(shí),為了避免崩潰發(fā)生,可以借助這個(gè)函數(shù)來判斷這個(gè)對象是否可以調(diào)用此可選方法。在運(yùn)行時(shí)層面,isMemberOfClass對比兩者的isa是否相同。respondsToSelector"則封裝了一個(gè) Objective-C 運(yùn)行時(shí)函數(shù):respondsToSelector,其接受 Selector 和類為參數(shù)。

如果您寫過單元測試的話,您就會(huì)知道當(dāng)我們在編寫XCTestCase的時(shí)候,需要完成setUp和tearDown的設(shè)定,隨后才能編寫相關(guān)的test函數(shù)。當(dāng)測試運(yùn)行的時(shí)候,系統(tǒng)會(huì)自行遍歷所有的測試函數(shù),并自動(dòng)運(yùn)行。這個(gè)功能是借助 Objective-C 的運(yùn)行時(shí)機(jī)制實(shí)現(xiàn)的。

unsignedintcount;Method*methods=class_copyMethodList(myObject.class,&count);//Ivar *list = class_copyIvarList(myObject.class,&count);for(unsignedi=0;i

我們可以復(fù)制方法列表,如果需要的話,還可以復(fù)制變量列表。可以獲取方法名,然后將其轉(zhuǎn)換為字符串,檢查其是否包含有 “test”,如果有便可以運(yùn)行?,F(xiàn)在我們便搭建好了XCTest的最簡單版本!

那么變量和方法是由什么組成的呢?

structobjc_ivar{char*ivar_name;char*ivar_type;intivar_offset;}structobjc_method{SELmethod_name;char*method_types;IMPmethod_imp;}

變量的組成與我們實(shí)際在代碼當(dāng)中所定義差別不大。其中包含了變量類型和變量名稱。偏移量 (offset) 則是內(nèi)存管理方面的內(nèi)容。

Objective-C 方法的名稱則是通過 Selector 來表示的,這也就是我們在performSelector當(dāng)中所匹配的內(nèi)容。同樣,方法還用編碼字符串來表示其類型。之后便是方法的實(shí)現(xiàn),它使用了一種特定的表示方式,對此我們不必去深究。

因此,方法是非常簡單的,我們同樣可以在運(yùn)行時(shí)向?qū)ο螽?dāng)中添加方法。

MethoddoStuff=class_getInstanceMethod(self.class,@selector(doStuff));IMPdoStuffImplementation=method_getImplementation(doStuff);constchar*types=method_getTypeEncoding(doStuff);//“v@:@"class_addMethod(myClass.class,@selector(doStuff:),doStuffImplementation,types);

實(shí)現(xiàn)這個(gè)功能,我們需要用到class_addMethod這個(gè)函數(shù)。它所需的參數(shù)全都是我們之前所說的,方法結(jié)構(gòu)體當(dāng)中的那三個(gè)值:Selector、方法實(shí)現(xiàn)和方法類型。具體的方法實(shí)現(xiàn)部分我們?nèi)×藗€(gè)巧,因?yàn)槲覀兪褂昧思扔械膁oStuff方法,因此能夠很簡單地獲取其方法實(shí)現(xiàn)和方法類型,不過我們還可以用其他方法來完成。

當(dāng)然,我們添加了方法目的就是要使用它們。我們可以使用[self doStuff]或者[self performSelector:@selector(doStuff)]來進(jìn)行調(diào)用,實(shí)際上在運(yùn)行時(shí)級別,它們都是借助objc_msgSend向?qū)ο蟀l(fā)送了一個(gè)消息。

[selfdoStuff];[selfperformSelector:@selector(doStuff)];objc_msgSend(self,@selector(message));

但是如果調(diào)用方法所在的對象為nil的時(shí)候,我們就會(huì)得到一個(gè)異常,應(yīng)用便會(huì)崩潰。但事實(shí)證明,在崩潰之前會(huì)預(yù)留幾個(gè)步驟,從而允許我們對某個(gè)不存在的函數(shù)進(jìn)行一些操作。

方法轉(zhuǎn)發(fā)(9:24)

我們可以將方法轉(zhuǎn)發(fā) (forward) 給其余目標(biāo)。當(dāng)我們試圖橋接兩個(gè)不同的框架的時(shí)候,這個(gè)功能便非常有用。當(dāng)我們調(diào)用某個(gè)未實(shí)現(xiàn)的方法時(shí),這便是會(huì)發(fā)生的操作。

// 1+(BOOL)resolveInstanceMethod:(SEL)sel{// 添加實(shí)例方法并返回 YES 的一次機(jī)會(huì),它隨后會(huì)再次嘗試發(fā)送消息}// 2-(id)forwardingTargetForSelector:(SEL)aSelector{// 返回可以處理 Selector 的對象}// 3-(NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector{// 您需要實(shí)現(xiàn)它來創(chuàng)建 NSInvocation}-(void)forwardInvocation:(NSInvocation*)invocation{// 在您所選擇的目標(biāo)上調(diào)用 Selector[invocationinvokeWithTarget:target];}

當(dāng)您調(diào)用了某個(gè)不存在的方法時(shí),運(yùn)行時(shí)首先會(huì)調(diào)用一個(gè)名為resolveInstanceMethod的類方法,如果所調(diào)用的方法是類方法的話,則為調(diào)用resolveClassMethod。這時(shí)候我們便有機(jī)會(huì)來添加方法了,步驟的話我們之前就已經(jīng)展示過了。如果我們返回了YES,就意味著原始方法將會(huì)再次被調(diào)用。

如果您不想創(chuàng)建新方法的話,我們還有forwardingTargetForSelector。您可以直接返回需要調(diào)用方法的目標(biāo)對象即可,之后這個(gè)對象就會(huì)調(diào)用 Selector。

此外還有一個(gè)略為復(fù)雜的forwardInvocation。所有的調(diào)用過程都被封裝到NSInvocation對象當(dāng)中,之后您便可以使用特定的對象進(jìn)行調(diào)用了。如果您需要這么做,那么還需要實(shí)現(xiàn)methodSignatureForSelector。

因此,我們便可以將方法轉(zhuǎn)發(fā)給其他對象,但是您也可以替換或者交換方法的實(shí)現(xiàn)。您可以使用運(yùn)行時(shí)當(dāng)中最著名的動(dòng)態(tài)特性:方法混淆 (swizzling)?;煜幕痉椒ㄈ缦滤荆?/p>

+(void)load{staticdispatch_once_tonceToken;dispatch_once(&onceToken,^{Classclass=[selfclass];SELoriginalSelector=@selector(doSomething);SELswizzledSelector=@selector(mo_doSomething);MethodoriginalMethod=class_getInstanceMethod(class,originalSelector);MethodswizzledMethod=class_getInstanceMethod(class,swizzledSelector);BOOLdidAddMethod=class_addMethod(class,originalSelector,method_getImplementation(swizzledMethod),method_getTypeEncoding(swizzledMethod));if(didAddMethod){class_replaceMethod(class,swizzledSelector,method_getImplementation(originalMethod),method_getTypeEncoding(originalMethod));}else{method_exchangeImplementations(originalMethod,swizzledMethod);}});}

當(dāng)類加載之后,會(huì)調(diào)用一個(gè)名為load的類函數(shù)。由于我們只打算混淆一次,因此我們需要使用dispatch_once。接著我們便可以得到該方法,然后使用class_replaceMethod或者method_exchangeImplementations來替換方法。之所以想要混淆,是因?yàn)樗梢杂糜谌罩居涗浐?Mock 測試。

Foundation(11:15)

從運(yùn)行時(shí)的層面,我們往上一層,便來到了 Foundation 框架。Foundation 框架實(shí)現(xiàn)了基于運(yùn)行時(shí)的一個(gè)特性:鍵值編碼(key-value-coding, KVC) 以及鍵值觀察(key-value observing, KVO)。KVC 和 KVO 允許我們將 UI 和數(shù)據(jù)進(jìn)行綁定。這也是 Rx 以及其他響應(yīng)式框架實(shí)現(xiàn)的基礎(chǔ),這個(gè)基本的功能是內(nèi)含在 Foundation 當(dāng)中的。KVC 的工作方式如下所示:

@property(nonatomic,strong)NSNumber*number;[myClassvalueForKey:@"number"];[myClasssetValue:@(4)forKey:@"number"];

例如,假設(shè)我們有這個(gè)number屬性,您可以將屬性名稱作為鍵,來獲取屬性值或者設(shè)置屬性值。這個(gè)功能可以用在此前我們所看到的獲取變量列表、協(xié)議列表,以及危險(xiǎn)的混淆功能當(dāng)中。

接下來是 KVO,您可以對狀態(tài)的變化進(jìn)行注冊。

[myClassaddObserver:selfforKeyPath:@"number"options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNewcontext:nil];-(void)observeValueForKeyPath:(NSString*)keyPathofObject:(id)objectchange:(NSDictionary*)changecontext:(void*)context{// Respond to observation.}

在觀察的值發(fā)生變更之后,KVO 會(huì)調(diào)用此方法立即通知觀察者。通過這個(gè)方法,我們便可以按需更新 UI。

我們通常所說的 Objective-C 「動(dòng)態(tài)性」,往往都是指 KVO。雖然還有其余的函數(shù),但是這些是最常見、最常用的。這也就是人們所說的,Swift 缺失的部分。

話說回來,所有的這些操作都存有隱患。比方說 KVO,特別是當(dāng)我們對某個(gè)不是自己所創(chuàng)建的類進(jìn)行觀察時(shí),往往會(huì)發(fā)現(xiàn)有出乎意料的變化發(fā)生。通常而言,這些問題是非常難以調(diào)試的,也很難去理解為什么出錯(cuò)。在實(shí)際產(chǎn)品當(dāng)中,我并不建議使用它們,盡管它們非常好用。但是在實(shí)際產(chǎn)品當(dāng)中,我會(huì)很謹(jǐn)慎地去使用這些功能。

Apple 也是如此認(rèn)為的,因此它們在視圖控制器當(dāng)中添加了這個(gè)私有方法,可以使用 class-dump 來查看。

+(void)attentionClassDumpUser:yesItsUsAgain:althoughSwizzlingAndOverridingPrivateMethodsIsFun:itWasntMuchFunWhenYourAppStoppedWorking:pleaseRefrainFromDoingSoInTheFutureOkayThanksBye:

的確,很讓人抓狂。

Swift(13:29)

現(xiàn)在讓我們來談?wù)?Swift 吧。Swift 是一種強(qiáng)類型語言。類型靜態(tài),也就是說 Swift 的默認(rèn)類型是非常安全的。如果需要的話,不安全類型也是存在的,但是 Swift 仍然是盡力推動(dòng)我們使用安全的靜態(tài)類型。Swift 中的動(dòng)態(tài)性可以通過 Objective-C 運(yùn)行時(shí)來獲得。

本來這是很好的,但是 Swift 開源并遷移到 Linux 之后,由于 Linux 上的 Swift 并不提供 Objective-C 運(yùn)行時(shí),事情就大條了。社區(qū)的關(guān)鍵點(diǎn)在于,讓 Swift 未來能夠自己配備動(dòng)態(tài)性,而不是依賴于 Apple。

也就是說,Swift 當(dāng)中存在有這兩個(gè)修飾符@objc和@dynamic,此外我們同樣還可以訪問NSObject。@objc將您的 Swift API 暴露給 Objective-C 運(yùn)行時(shí),但是它仍然不能保證編譯器會(huì)嘗試對其進(jìn)行優(yōu)化。如果您真的想使用動(dòng)態(tài)功能的話,就需要使用@dynamic。一旦您使用了@dynamic修飾符之后,就不需要添加@objc了,因?yàn)樗呀?jīng)隱含在其中。

回到我們的動(dòng)態(tài)特性當(dāng)中,讓我們來看一看 Swift 當(dāng)中這些動(dòng)態(tài)特性是什么樣的。假設(shè)我們需要使用內(nèi)省機(jī)制、轉(zhuǎn)發(fā)方法、替換和綁定方法。方法的轉(zhuǎn)發(fā)實(shí)際上變化不大:

// 1overrideclassfuncresolveInstanceMethod(_sel:Selector!)->Bool{// 添加實(shí)例方法并返回 true 的一次機(jī)會(huì),它隨后會(huì)再次嘗試發(fā)送消息}// 2overridefuncforwardingTarget(foraSelector:Selector!)->Any?{// 返回可以處理 Selector 的對象}// 3 - Swift 不支持 NSInvocation

resolveInstanceMethod同樣會(huì)被調(diào)用,forwardingTarget看起來似乎更貼近于 Swift 3 風(fēng)格的 API。但是NSInvocation并不能在 Swift 當(dāng)中使用。我們同樣可以實(shí)現(xiàn)方法轉(zhuǎn)發(fā),因此看起來也不算太壞。

方法混淆變得有些困難。load在 Swift 不再會(huì)被調(diào)用,因此我們需要在initialize中進(jìn)行混淆。在 Objective-C 當(dāng)中,我們使用dispatch_once,但是自 Swift 3 之后,dispatch_once便不復(fù)存在于 Swift 當(dāng)中了。事情變得略為復(fù)雜。雖然對于特定類型的函數(shù)而言,我們?nèi)匀豢梢詫⑵涠x為動(dòng)態(tài)函數(shù),但是它會(huì)消除大部分混淆的功能。

對于內(nèi)省而言,我們有了一些新的東西。

ifselfisMyClass{// YAY}letmyString="myString";letmirror=Mirror(reflecting:myString)print(mirror.subjectType)// “String"letstring=String(reflecting:type(of:myString))// Swift.String// No native method introspection

is替代了isMemberOfClass,它同樣也可以對 Swift 值類型使用。我們可以對結(jié)構(gòu)體、枚舉以及其他 Swift 當(dāng)中的新類型使用is。此外還有一個(gè)新的映射 API,它主要針對于管道 (pipe) 和數(shù)據(jù)。

目前,我們沒有原生的辦法來實(shí)現(xiàn)內(nèi)省。這也預(yù)示著這個(gè)功能未來可能會(huì)出現(xiàn),但是目前我們還無法實(shí)現(xiàn)。這很令人沮喪,特別是當(dāng)您想到我們此前所實(shí)現(xiàn)的XCTestCase。如果您打算為 Linux 編寫單元測試的時(shí)候,就無法自動(dòng)遍歷所有的函數(shù)。您必須實(shí)現(xiàn)static var allTests,然后手動(dòng)列出所有的測試函數(shù)。這很糟糕。

那么 KVO 和 KVC 呢?KVO 的魅力在于,您可以在不是自己所創(chuàng)建的類當(dāng)中使用它,也可以只對您想要監(jiān)聽變化的類使用。KVO 和 KVC 在 Swift 被極大地削弱了。您所觀察的對象必須要繼承自NSObject,并且使用一個(gè) Objective-C 類型。您所觀察的變量必須要生命為dynamic。您必須要對想要觀察的事務(wù)了如指掌。

問題是 Swift 并沒有很好的替代方案。您可以使用 Rx 或者基于協(xié)議來觀察對象。但是語言自身是沒有原生的解決方案的。

Swift 是一個(gè)讓人興奮的語言,此外也有一個(gè)好消息。最近,在Swift 郵件列表中,Chris Lattner 認(rèn)為為 Swift 添加動(dòng)態(tài)功能是非常重要的。他還說,即便人們不同意「動(dòng)態(tài)性」的功能是什么,我們的關(guān)鍵在于要找一個(gè)原生的、流暢的、符合 Swift 風(fēng)格的方法來解決這些問題。

總結(jié)(18:11)

如今我們所知道的是,更豐富的動(dòng)態(tài)性被安排在了 Swift 4 的第二階段。我們目前正處于 Swift 4 的第一階段,他們的重點(diǎn)是保證 API 的穩(wěn)定性。他們會(huì)在 iOS 11 之前盡量完成,這是核心團(tuán)隊(duì)的重點(diǎn)之一,他們可能會(huì)首先考慮引入內(nèi)省開始。

還有一件事,也就是我所啟動(dòng)的一個(gè)開源項(xiàng)目。目前我正在開發(fā)一個(gè)名為ObjectiveKit的開源庫。我的想法是用一些符合 Swift 風(fēng)格的方法來暴露一些運(yùn)行時(shí)函數(shù),我覺得這將是一件很有趣的事。

總而言之,Objective-C 的動(dòng)態(tài)性無疑是非常強(qiáng)大的、極其有用,雖然也存在危險(xiǎn)性。Swift 目前沒有足夠的替代方案來解決這些問題,但是可以預(yù)見在不久的將來 Swift 的動(dòng)態(tài)性將會(huì)出現(xiàn),這是值得我們期待的。我一開始所承諾的貓咪 GIF 圖在這里,我覺得以它作為結(jié)尾是很好的想法。感謝大家!

Roy Marmelstein

Creator of PhoneNumberKit, Interpolate and Localize.

Twitter

Edited by Billy Leet

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容