ios runtime基礎知識

與Runtime交互


Objective-C程序有有三種與runtime系統交互的級別: 編程

  1. 經過Objective-C源代碼
  2. 經過Foundation庫中定義的NSObject提供的方法
  3. 經過直接調用runtime方法

經過Objective-C源代碼

在大多數的部分,運行時系統會自動運行並在後臺運行。咱們使用它只是寫源代碼並編譯源代碼。當編譯包含Objective-C類和方法的代碼時,編譯器會建立實現了語言動態特性的數據結構和函數調用。該數據結構捕獲在類、擴展和協議中所定義的信息。 數組

最重要的runtime函數是發消息函數,在編譯時,編譯器會轉換成相似objc_msgSend這樣的發送消息的函數。所以,咱們經過寫好源代碼,編譯器會自動幫助咱們編譯成runtime代碼。 緩存

經過NSObject提供的方法

在Cocoa編程中,大部分的類都繼承於NSObject,也就是說NSObject一般是根類,大部分的類都繼承於NSObject。有些特殊的狀況下,NSObject只是提供了它應該要作什麼的模板,卻沒有提供全部必須的代碼。 數據結構

有些NSObject提供的方法僅僅是爲了查詢運動時系統的相關信息,這此方法均可以反查本身。好比-isKindOfClass:和-isMemberOfClass:都是用於查詢在繼承體系中的位置。-respondsToSelector:指明是否接受特定的消息。+conformsToProtocol:指明是否要求實如今指定的協議中聲明的方法。-methodForSelector:提供方法實現的地址。 函數

經過直接調用runtime函數

runtime庫函數在usr/include/objc目錄下,咱們主要關注是這兩個頭文件: this

1
2
3
4
 
#import <objc/runtime.h>
#import <objc/objc.h>
 

關於如何使用,後續的文章再細細講解。 編碼

消息(Message)


爲何叫消息呢?由於面向對象編程中,對象調用方法叫作發送消息。在編譯時,應用的源代碼就會被編將對象發送消息轉換成runtime的objc_msgSend函數調用。 spa

在Objective-C,消息在運行時並不要求實現。編譯器會轉換消息表達式: 指針

1
2
3
 
[ receiver message ] ;
 

在編譯時會轉換成相似這樣的函數調用: code

1
2
3
 
objc_msgSend ( receiver , selector ) ;
 

具體會轉換成哪一個,咱們來看看官方的原話:

1
2
3
4
5
6
7
 
When it encounters a method call , the compiler generates a call to one of the
*    functions \ c objc_msgSend , \ c objc_msgSend_stret , \ c objc_msgSendSuper , or \ c objc_msgSendSuper_stret .
*    Messages sent to an objects superclass ( using the \ c super keyword ) are sent using \ c objc_msgSendSuper ;
*    other messages are sent using \ c objc_msgSend . Methods that have data structures as return values
*    are sent using \ c objc_msgSendSuper_stret and \ c objc_msgSend_stret .
 

也就是說,咱們是經過編譯器來自動轉換成運行時代碼時,它會根據類型自動轉換成下面的其它一個函數:

  • objc_msgSend:其它普通的消息都會經過該函數來發送
  • objcmsgSendstret:消息中須要有數據結構做爲返回值時,會經過該函數來發送消息並接收返回值
  • objcmsgSendSuper:與objcmsgSend函數相似,只是它把消息發送給父類實例
  • objcmsgSendSuperstret:與objcmsgSendstret函數相似,只是它把消息發送給父類實例並接收數組結構做爲返回值

另外,若是函數返回值是浮點類型,官方說明以下:

1
2
3
4
5
6
7
8
9
 
* arm :    objc_msgSend_fpret not used
* i386 :   objc_msgSend_fpret used for ` float ` , ` double ` , ` long double ` .
* x86 - 64 : objc_msgSend_fpret used for ` long double ` .
*
* arm :    objc_msgSend_fp2ret not used
* i386 :   objc_msgSend_fp2ret not used
* x86 - 64 : objc_msgSend_fp2ret used for ` _Complex long double ` .
 

其實這是一個條件編譯,咱們不用擔憂是哪一種處理器上,咱們只須要調用objc_msgSend_fpret函數便可。

當消息被髮送到實例對象時,它是如何處理的:

image

咱們的根類是NSObject,它會一層一層的傳遞,直接找到要處理該消息的對象,若都沒有找到,正常狀況下會出現Unreconized selector ...這樣的崩潰提示了。

Message Forwarding


當發送消息給一個不處理該消息的對象是錯誤的。而後在宣佈錯誤以前,運行時系統給了接收消息的對象處理消息的第二個機會。

當某對象不處理某消息時,能夠經過重寫-forwardInvocation:方法來提供一個默認的消息響應或者避免出錯。當對象中找不到方法實現時,會按照類繼承關係一層層往上找。咱們看看類繼承關係圖:

image

全部元類中的isa指針都指向根元類,而根元類的isa指針則指向自身。根元類是繼承於根類的,與根類的結構體成員一致,都是objc_class結構體,不一樣的是根元類的isa指針指向自身,而根類的isa指針爲nil

咱們再看看消息處理流程:

image

當對象查詢不到相關的方法,消息得不到該對象處理,會啓動「消息轉發」機制。消息轉發還分爲幾個階段:先詢問receiver或者說是它所屬的類是否能動態添加方法,以處理當前這個消息,這叫作「動態方法解析」,runtime會經過+resolveInstanceMethod:判斷可否處理。若是runtime完成動態添加方法的詢問以後,receiver仍然沒法正常響應則Runtime會繼續向receiver詢問是否有其它對象即其它receiver能處理這條消息,若返回可以處理的對象,Runtime會把消息轉給返回的對象,消息轉發流程也就結束。若無對象返回,Runtime會把消息有關的所有細節都封裝到NSInvocation對象中,再給receiver最後一次機會,令其設法解決當前還未處理的這條消息。

消息處理越日後,開銷也就會越大,所以最好直接在第一步就能夠獲得消息處理。

咱們看看類結構體:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 
struct objc _ class {
     Class isa   OBJC_ISA_AVAILABILITY ;
 
#if !__OBJC2__
     Class super_class                                         OBJC2_UNAVAILABLE ;
     const char *name                                          OBJC2_UNAVAILABLE ;
     long version                                             OBJC2_UNAVAILABLE ;
     long info                                                 OBJC2_UNAVAILABLE ;
     long instance_size                                       OBJC2_UNAVAILABLE ;
     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 ;
#endif
 
} OBJC2_UNAVAILABLE ;
/* Use `Class` instead of `struct objc_class *` */
 

咱們能夠看到每一個類結構體都會有一個isa指針,它是指向元類的。它還有一個父類指針super_class,指針父類。包含了類的名稱name、類的版本信息version、類的一些標識信息info、實例大小instance_size、成員變量地址列表ivars、方法地址列表methodLists、緩存最近使用的方法地址cache、協議列表protocols`。

咱們在使用時,常用到Class,它就是:

1
2
3
 
typedef struct objc_class *Class ;
 

當類爲根類時,它的super_class就會是nil。普通的Class存儲的是實例成員,如-號方法、屬性、成員變量,而isa則指向元類,而元類存儲的是靜態成員,如+號方法、static成員。

Type Encoding


編碼值 含意
c 表明char類型
i 表明int類型
s 表明short類型
l 表明long類型,在64位處理器上也是按照32位處理
q 表明long long類型
C 表明unsigned char類型
I 表明unsigned int類型
S 表明unsigned short類型
L 表明unsigned long類型
Q 表明unsigned long long類型
f 表明float類型
d 表明double類型
B 表明C++中的bool或者C99中的_Bool
v 表明void類型
* 表明char *類型
@ 表明對象類型
# 表明類對象 (Class)
: 表明方法selector (SEL)
[array type] 表明array
{name=type…} 表明結構體
(name=type…) 表明union
bnum A bit field of num bits
type A pointer to type
? An unknown type (among other things, this code is used for function pointers)

咱們想要經過運行時處理各類類型,那麼咱們必需要知道哪一種字符表明什麼類型。

相關文章
相關標籤/搜索