成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Objective-C 與 Runtime:為什么是這樣?

移動開發
筆者非常高興能為Objective-C寫寫自己的理解和總結,不僅僅因為是筆者是Objective-C多年的重度開發者,更是因為這是一門有獨特想法的,有創造性的,有優美語法的,有歷史地位的編程語言。如果說對本文有什么預期的話,筆者希望能把一些類似“為什么是這樣”的問題說清楚。

[[148060]]

筆者非常高興能為Objective-C寫寫自己的理解和總結,不僅僅因為是筆者是Objective-C多年的重度開發者,更是因為這是一門有獨特想法的,有創造性的,有優美語法的,有歷史地位的編程語言。如果說對本文有什么預期的話,筆者希望能把一些類似“為什么是這樣”的問題說清楚。

Objective-C發明于上世紀80年代,Objective-C的作者——Brad Cox和Tom Love,在接觸到SmallTalk語言之后,一方面受到SmallTalk的啟發,另一方面也是看好C語言有著巨大影響力和廣闊前景,因此選擇在C語言的基礎上引入SmallTalk語言面向對象和消息派發的概念。最初的版本以C語言的擴展的形式實現的,在C編譯器中編寫支持Objective-C的預處理模塊,預處理會先將Objective-C語法代碼轉化為C代碼,再繼續C代碼的編譯過程。1988年,以企業為目標客戶的NeXT公司購買Objective-C的使用授權,接著擴展著名開源編譯器GCC,使其支持Objective-C,并且開發了AppKit和FoundationKit等基礎庫,Objective-C成為了NeXTSTEP系統(工作站)上“標準”的應用程序開發語言。1996年,Apple公司收購了NeXT公司,NeXTSTEP/OPENSTEP系統成為Apple新一代操作系統OS X的研發基礎。 2005年,Apple引入了Chris Lattner以及他的LLVM技術團隊,Objective-C新特性和編譯優化***次得到高水平編譯器***優先級的支持,先從后端的代碼優化和生成開始,逐步擴展到前端的語法解析(Clang)。如今(2015),Objective-C已經擁有GCC之外更為適合更為優異的編譯器套裝選擇——LLVM編譯器,LLVM包括完整的前后端模塊,***版本6.1(2015)。

Objective-C是面向對象的,這是Objective-C最基本的的概念。關于面向對象,把一定的算法(函數)和數據(變量)因某種內在的聯系綁定在一起,形成最基本的程序結構單元,這些結構單元即是經常談及的對象,加上抽象二字,我們稱呼它為抽象對象,術語簡稱類;通過對變量的賦值(筆者認為不僅是變量,邏輯運算如閉包也是可以用于賦值)則會構成實體對象,術語簡稱對象(Objective-C一般也稱作實例)。對象和對象之間不是完全獨立的,通過巧妙的方式,它們之間能建立緊密的聯系,比如繼承、派生,對事物的抽象以及對代碼的復用有著微妙而重大的價值。Brad Cox和Tom Lov出版的***本正式Objective-C著作,書名即為《Object-Oriented Programming, An Evolutionary Approach》。那么,為什么要對象,為什么要面向對象?這是個好問題,觀察人類普遍的思維,我們理解這個世界使用最多的概念就是物體,我們擅長把感知到的一切抽象為一個個的物體,通過了解物體的構成,以及物體之間的作用關系,實現對這個世界的認知和作用的目的。這一直是非常奏效的!面向對象就是把人類的思維的天賦和積累的思想財富應用于編程,這樣,程序對于增強生產能力/提高生活品質的效率和能力方面會大大提高。

blob.png

/* 上圖為FoundationKit中支持的集合對象——(不可變)數組,繼承于根類NSObject,支持實現NSCopying在內的一系列協議(接口),count代表著有一個只讀變量,- (id)objectAtIndex:(NSUInteger)index等表示數組支持的可供使用的方法(函數) */

消息派發是Objective-C函數(Objective-C實際稱方法)調用的模式,前文亦有提及,概念繼承于Smalltalk。Objective-C的對象相互調用函數,被看做是向目標對象傳遞消息,消息的發送者稱作sender,消息的接收者稱作receiver,消息中間傳遞的字符串稱作selector(選擇子)。

blob.png

/* 上圖的代碼表示至少有兩個明顯receiver,self.view為其中一個消息接收者,傳遞的消息(字符串/選擇子)為 “setBackgroundColor:“,UIColor表示一個類,類也是可以作為消息的接收者,字符串/選擇子為 “yellowColor” */

消息的處理就是需要先確定實際執行的方法然后跳轉過去并執行,我們理解為這是對該消息的回應,編譯期間,單從一句”派發消息”的語法是無法確定實際執行的結果。只有在程序運行期間,實際執行的結果才能得到確定。這種在運行期間才確定實際執行的方法,Objective-C稱為動態綁定。消息派發這種工作機制明顯區別另一著名面向對象編程語言——C++。C++調用對象的函數,函數與對象之間的關系,在編譯期間就必須嚴格確定,如果car里面沒有定義函數名為fly的函數,編譯器不會通過,而是會報錯。Objective-C如果向car發送字符串為”fly”的selector,即使car沒有實現fly方法,編譯器依然能夠通過,但是運行期間則會因為獲取不到實際執行的方法而拋出異常。這也就是說,消息派發的設計使得編譯期間Objective-C非常包容對象所屬的類。如上述,相同對象有相同的定義,稱為類,類本身還可以看作對象——“類”對象,可以對“類”對象進行“類”的定義,比如比較運算,哈希,描述,類名等,總之一切皆為對象。C++里面我們可以基于稱之為模板的方式實現對“類”的自定義,Objective-C通過統一基類比如NSObject(不僅僅只有NSObject,還可以是各類根協議)方式對所有類新增定義。你可以向任何包括空指針nil在內的對象發你想發的消息。消息派發的機制使得在不重新編譯的情況下,在運行期間,干預或者說hook原來的target(方法、變量等)變得更易于實現,更有實際應用價值。這個是需要依賴于消息派發和動態綁定的實現機制——Runtime,但是Runtime并不僅僅為消息派發和動態綁定而work,它也是Objective-C面向對象、內存模型等特性的實現者。

在正式介紹Runtime之前,我們先繼續介紹Objective-C的另外一個重要概念,筆者要說是Objective-C內存管理模型,程序運行時,創建一個對象總是要占用內存的,而內存總大小總歸有限,所以當一個對象不再被需要時,應當及時回收它所占用的內存資源用于新的對象,Objective-C的內存管理原理,簡單說就是“引用計數”機制。如果有模塊需要引用一個對象,引用時會讓對象統計用的引用計數值加1,并記錄在對象的結構信息當中,當模塊不再需要該對象的時候則減1,而當該對象的引用計數值為0時,就可以認為該對象不再被需要,及時銷毀釋放內存(回收資源)。Objective-C對象的內存空間僅分配在“堆空間”(heap space)中,肯定是不會分配在“棧”(stack)上。我們知道,“棧”的占用和回收是有嚴格的數據操作規則,簡稱“先入后出”。函數執行時,傳入的變量(當然包括對象變量)會按照確定的序列規則自動壓入“棧”(占有內存資源),函數執行結束時,這些變量又會按照相反的序列規則自動彈出(釋放內存資源)。因此,我們可以看出,“棧”其實是無法實施“引用計數”機制的,Objective-C否定使用“棧”存儲對象的設想。在語法上,Objective-C也無法像C++那樣直接聲明并創建一個對象變量,更無法直接操作該對象,Objective-C都是需要以類似C語言申請堆內存塊的語法(alloc)那樣創建一個對象變量,并且必須通過對象指針作為訪問句柄,這跟C語言申請堆內存塊非常類似。Objective-C這一“任性”的設計,也使得對象嵌套(一個對象作為另一個對象的成員變量)時,對象基于引用計數機制,其成員變量也必須遞歸地遵循引用計數機制。因為成員變量實際都是一枚枚對象指針,很可能是與其它對象共享同一個對象(指針都指向同一塊內存),引用計數機制正是適合用于支持這種“共享”內存的管理。需要特別說明,如果可以像C++那樣創建一個對象變量做成員變量,那么該成員變量會被存儲在該對象所在的一塊連續內存塊,該對象銷毀時能夠自動把成員變量的占有的內存塊全部釋放收回,這與引用計數的機制并不太符合,所以,在Objective-C中對象變量不被支持也進一步得到理解。

blob.png

blob.png

/* 上圖的接口(方法)是Objective-C中內存管理相關的接口 (方法)*/

Runtime(component)譯名一般稱為運行期組件,一個純C語言寫成的基礎庫(lib),Objective-C編寫出來的程序必須得到Runtime的運行才能正常work,在Java、PHP或者Flash之類的編程語言當中,大家對于Runtime并不會太陌生,Objective-C的Runtime其實也是一回事。正是Runtime實現了Objective-C許多的特性,Objective-C面向對象、消息派發、動態綁定和內存管理都與Runtime的息息相關。那么,在Objective-C當中,對象、類、函數(方法)都是如何被構造并發揮作用的?前文提及,面向對象中的類,被看作抽象了的對象,Runtime也是秉持這一理念。Runtime是純C寫成,用struct結構體來描述對象(實體對象)和類(抽象對象)。

blob.png

blob.png

對象的struct比較簡單,用*id作為結構體objc_object的指針別名,***struct成員isa是Class類型的指針變量,正是該變量確定了對象所屬的類。Class類型也是struct,是結構體objc_class的指針別名,用于描述類構成的struct,***成員isa也是Class類型的指針變量(由兩個結構體的***成員均為Class類型的指針變量的設計使得我們能進一步體會到Runtime中,類的確有著和對象相同的看待),類的isa會指向稱之為metaclass(元類)的struct,metaclass抽象了類的特性,metaclass的***成員自然也是isa的Class類型的指針變量,不同的是元類的isa最終指向的是它自身,由此我們可以觀察到,類struct是一種遞歸嵌套的設計,它正體現了面向對象***抽象的理念,最終實現上指向自己則是實際工程處理的需要。一般我們還認為objc_class這個struct存放類的metadata(元數據),例如類的實例方法、類的實例變量以及類的超類指針等。

blob.png

Runtime還允許我們通過標準的接口(C函數)對所有Objective-C類的變量、方法、屬性以及協議等等作查詢和動態擴展,從而達到我們豐富項目中語言和類庫特性的目標。

blob.png

/* 上圖的通過標準的Runtime API(C函數)打印UIKit中UIView的所有變量、屬性以及方法*/

Runtime的另外一個重要的特性實現即為消息派發,objc_msgSend是消息派發最核心最基礎的入口函數,除此之外還有objc_msgsend_stret,objc_msgSend_fpret,objc_msgSendSuper等函數,然而它們的重要性和作用遠不及objc_msgSend。objc_msgSend函數會依據receiver和selector的來調用適當的方法。為了完成此操作,該函數需要在recevier所屬的類中搜尋其“方法列表”,如果能找到與selector字符串名稱相符的方法,就跳轉至該方法。若是找不到,那就沿著繼承體系繼續向上查找,等找到合適的方法之后再跳轉。如果最終還是找不到相符的方法,那就執行“消息轉發”操作。由此,我們可以看到,調用一個方法似乎需要相當的步驟。每一個步驟都是開銷,是否會導致Objective-C有性能問題?所幸obj_msgSend會將匹配到得結果緩存在“快速映射表”(fast map),每個類都有這樣一塊緩存,若是后面還需要向該類發送和相同的selector消息,執行起來將會快許多。當然,這種“快速執行路徑”(fast path)還是不如“靜態綁定函數調用”(statically bound function call)那樣快,不過通過匯編等優化技術,映射表的查詢開銷已非常小,可以說,即使相比較C++的靜態綁定,Objective-C的消息派發機制已經不是性能瓶頸所在。如果說以上的消息派發機制就是Objective-C動態綁定的全部內容,其實并不完全。當對象查詢不到相關的方法,消息無法正確回應時,還會啟動“消息轉發”機制。是的,在支持“動態增加和替換”的方法列表之外,我們還能夠提供其它的正常響應方式。消息轉發還分為幾個階段,***,先詢問receiver或者說是它所屬的類,看其是否能動態添加方法,以處理當前這個“未知選擇子”(unkonwn selector),這叫做“動態方法解析”(dynamic method resolution),Runtime會通過回調一個類方法來尋求動態添加方法的支持。如果Runtime完成動態添加方法的詢問之后,receiver仍然無法正常響應,則Runtime會繼續向receiver詢問是否有其它對象即其它receiver能處理這條消息,若返回能夠處理的對象,Runtime會把消息轉給返回的對象,消息轉發流程也就結束。若無對象返回,Runtime會把消息有關的全部細節都封裝到NSInvocation對象中,再給receiver***一次機會,令其設法解決當前還未處理的這條消息。消息轉發的流程可以歸納到以下圖表:

blob.png

由圖表可以看出,receiver在每一步中均有機會處理消息,步驟越往后,處理消息累計開銷就越大。所以,***能在***步就處理完,這樣的話,Runtime還可以把方法進行緩存,在一步到位的同時進一步降低***查詢這樣的開銷。需要注意的是在***一個階段,需要由兩個接口一起完成,先要通過- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector接口返回格式化的方法對象,下一個接口- (void)forwardInvocation:(NSInvocation *)anInvocation中傳入參數NSInvocation對象對此方法對象是有依賴,***步的NSMethodSignature對象返回nil,則消息轉發流程即告結束。

***利用消息轉發機制,我們實現一個讓NSString類支持NSArray實例方法的例子,這對于降低程序的Crash率很有幫助:

我們先實現一個方法替換的接口swizzle method,幫助我們在不需要繼承的情況下,實現對父類方法的代碼注入

blob.png

通過swizzle方式(class_addMethod、class_replaceMethod、method_exchangeImplementations),在NSString類的resolveInstanceMethod:中,動態方法解析的方式注入3個NSArray的實例方法:

blob.png

測試用例:

blob.png

測試結果:

blob.png

結尾,筆者用了很大的篇幅和代碼片段嘗試去解釋Objective-C最基本的一些概念,包括面向對象、消息派發、內存管理等等,并且也討論了這些概念在Rumtime上的實現,這當中還不包括屬性、分類、類族、協議等Objective-C中同樣重要的feature,也沒有深入闡述其中的一些編碼細節(有關編碼,通過搜索引擎,總能獲取許多令人滿意的答案)。筆者更多地是希望在有限的篇幅中幫助讀者快速理解Objective-C,理解它為什么是這樣而不是那樣,并且對于想進一步學習和使用Objective-C的開發者和工程師能有所幫助。

參考鏈接(部分):

責任編輯:倪明 來源: springox的博客
相關推薦

2012-03-07 13:43:59

Objective-C

2014-04-01 10:50:42

iOS開發runtimeObjective-C

2014-07-14 09:58:18

Objective-CiOS學習

2015-07-08 10:51:27

Objective-CRuntime

2011-08-04 13:38:01

Objective-C C++

2013-06-20 10:40:32

Objective-C實現截圖

2013-03-27 12:54:00

iOS開發Objective-C

2011-05-11 15:58:34

Objective-C

2011-05-11 11:20:26

Objective-C

2011-08-16 13:43:40

Objective-C文件cocoa

2011-08-10 18:07:29

Objective-C反射

2017-02-10 09:55:53

SwiftObjective-C

2014-09-24 11:15:05

Objective-CSwift

2011-07-07 17:04:33

iPhone Action Objective-

2011-07-18 14:59:20

iPhone Objective-

2011-07-25 13:05:37

Objective-C 委托

2012-04-23 11:00:56

iOS開發Objective-CJavaScript

2011-08-03 16:55:05

Objective-C 代理

2011-08-04 09:35:09

Objective-C 編碼規范

2014-04-30 10:16:04

Objective-CiOS語法
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 天天操操 | 黑人一级黄色大片 | 五月天天丁香婷婷在线中 | 成人午夜在线 | www.久久精品视频 | 青青草av在线播放 | 国产精品视屏 | 国产精品福利网站 | 91青娱乐在线 | 99在线免费观看视频 | 成人黄色三级毛片 | 91精品国产91久久久久久三级 | 自拍视频网站 | 日韩视频精品在线 | 国产精品久久9 | 麻豆视频在线免费观看 | 久久成人国产 | 国产视频三级 | 免费国产成人av | 日本一二三区电影 | 国产欧美日韩在线观看 | 日韩免费在线视频 | 久久亚 | 国产在线精品一区 | 波多野结衣一区二区 | 亚洲色欧美另类 | 免费观看一级特黄欧美大片 | 国产成人99久久亚洲综合精品 | 人人操日日干 | 国产探花在线精品一区二区 | 天天天操天天天干 | 国产黑丝在线 | 三级黄片毛片 | 一区二区三区视频在线免费观看 | 99色视频| 中文日韩在线 | 亚洲高清久久 | 99精品福利视频 | 久久久婷 | 亚洲网站在线观看 | 中文字幕亚洲视频 |