Objective-C程序有有三種與runtime系統交互的級別: 編程
在大多數的部分,運行時系統會自動運行並在後臺運行。咱們使用它只是寫源代碼並編譯源代碼。當編譯包含Objective-C類和方法的代碼時,編譯器會建立實現了語言動態特性的數據結構和函數調用。該數據結構捕獲在類、擴展和協議中所定義的信息。 數組
最重要的runtime函數是發消息函數,在編譯時,編譯器會轉換成相似objc_msgSend這樣的發送消息的函數。所以,咱們經過寫好源代碼,編譯器會自動幫助咱們編譯成runtime代碼。 緩存
在Cocoa編程中,大部分的類都繼承於NSObject,也就是說NSObject一般是根類,大部分的類都繼承於NSObject。有些特殊的狀況下,NSObject只是提供了它應該要作什麼的模板,卻沒有提供全部必須的代碼。 數據結構
有些NSObject提供的方法僅僅是爲了查詢運動時系統的相關信息,這此方法均可以反查本身。好比-isKindOfClass:和-isMemberOfClass:都是用於查詢在繼承體系中的位置。-respondsToSelector:指明是否接受特定的消息。+conformsToProtocol:指明是否要求實如今指定的協議中聲明的方法。-methodForSelector:提供方法實現的地址。 函數
runtime庫函數在usr/include/objc目錄下,咱們主要關注是這兩個頭文件: this
1
2
3
4
|
#import <objc/runtime.h>
#import <objc/objc.h>
|
關於如何使用,後續的文章再細細講解。 編碼
爲何叫消息呢?由於面向對象編程中,對象調用方法叫作發送消息。在編譯時,應用的源代碼就會被編將對象發送消息轉換成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
object’
s
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
.
|
也就是說,咱們是經過編譯器來自動轉換成運行時代碼時,它會根據類型自動轉換成下面的其它一個函數:
另外,若是函數返回值是浮點類型,官方說明以下:
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函數便可。
當消息被髮送到實例對象時,它是如何處理的:
咱們的根類是NSObject,它會一層一層的傳遞,直接找到要處理該消息的對象,若都沒有找到,正常狀況下會出現Unreconized selector ...這樣的崩潰提示了。
當發送消息給一個不處理該消息的對象是錯誤的。而後在宣佈錯誤以前,運行時系統給了接收消息的對象處理消息的第二個機會。
當某對象不處理某消息時,能夠經過重寫-forwardInvocation:方法來提供一個默認的消息響應或者避免出錯。當對象中找不到方法實現時,會按照類繼承關係一層層往上找。咱們看看類繼承關係圖:
全部元類中的isa指針都指向根元類,而根元類的isa指針則指向自身。根元類是繼承於根類的,與根類的結構體成員一致,都是objc_class結構體,不一樣的是根元類的isa指針指向自身,而根類的isa指針爲nil
咱們再看看消息處理流程:
當對象查詢不到相關的方法,消息得不到該對象處理,會啓動「消息轉發」機制。消息轉發還分爲幾個階段:先詢問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成員。
編碼值 | 含意 |
---|---|
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) |
咱們想要經過運行時處理各類類型,那麼咱們必需要知道哪一種字符表明什麼類型。