1,動態分配
動態分配由兩個函數來處理:malloc和free。使用一個參數來調用malloc以請求記憶體,該函數指定了所需的位元組數。malloc返回指向請求的數目的位元組的一個指標,隨後可以將這個指標強制轉換成想要的資料類型,並且賦給一個變數,而這個變數的類型就是指向所請求的類型的一個指標。曾今,當沒有可用的記憶體配置時,malloc返回NULL,然而,OS X和iOS使用延遲分配,malloc返回所請求的記憶體一個指標,但是,在執行一段代碼來訪問該記憶體之前,系統不會為該請求分配記憶體資源。結果,如果你多次請求記憶體,並且沒有使用該記憶體或沒有釋放該記憶體,那麼malloc可能會返回一個非NULL的值,即便沒有更多的記憶體也如此。在這種情況下,使用返回的指標可能導致程式崩潰。假設給RAM和交換空間指定一些典型值,即便有可能,你也是極少會看到這種情況在OS X系統上發生。在iOS中,遇到這種情況之前,應用程式很可能會從系統接收到一條低記憶體警告。
2,編譯器指令
以@字元開頭的單詞是編譯器指令,而不是可執行代碼。我們知道,@interface表示一個類定義的介面部分的開始;還有@implementation表示實現部分的開始;還有@end,用來表示這些部分的結束。objective-c使用NSString的常量執行個體而不是普通的C字串,而NSString是在Foundation架構中定義的一個類。可以用建立一個C字串的方式來建立一個NNstring直接量,只不過在字串開始處添加一個@,例如:
"the big apple" // literal C string
@"the big apple" //literal NSString
嚴格地講, @"the big apple" 是一條編譯指令,它告訴編譯器建立一個NSString直接量,其文本為“the big apple”,如果使用帶有%@描述符的格式字串,但是忘記了提供一個對應的對象參數,那麼NSLog將嘗試向位於對象參數所應該放置的地址的位元組發送一條description訊息。這通常會導致程式崩潰。
3,訊息
要求Objective-c對象執行自己的一個方法,這與調用一個函數並不是同樣的事情。函數調用通常是靜態繫結的,即在運行時所發生的事情,在編譯時間就已經確定了。當編譯器遇到一個函數調用時,它插入一條指令,以跳轉到構成函數體的代碼。而Objective-c使用一種叫訊息的更為動態系統。當想要objective-c對象執行自己的一個方法時,就向該對象發送一條訊息。對象通過執行於該訊息對應的方法來做出相應。
訊息的運算式的形式如下:
【receive message】
當執行一個訊息運算式時,objective-c 運行時檢查接收者並確定其類。運行時針對每個類都有一個表,列出了該類定義的方法,並且將每個方法與指向該方法的代碼的一個指標關聯起來。運行時從訊息擷取方法名,在接收者的類表中找到相對應的指標,然後使用該指標來執行方法的代碼。
objective-c訊息系統時如何工作的?
具體細節如下:1,所有的objective-c對象知道他們自己的類。要完成這一任務,編譯器給每個對象執行個體添加一個執行個體變數isa,isa說明一個執行個體時什麼類型的對象。在運行時,isa指向類的一個類結構,這是一種不透明的類型,其中包括了有關類的資訊。包括該類所定義的執行個體變數和方法;並且還有一個指標,指向關於其超類的資訊。當啟動程式時,這些資訊在運行時填充。2,編譯器將方法轉換為實際編譯的C函數。在這一過程中,它在參數列表的開始處添加兩個額外的參數,self和_cmd。self時一個指標,指向發送訊息時從當接收者的對像;_cmd是訊息的方法名稱對應的選折器。
考慮如下的objective-c方法:-(void)dosomething:(int)anarg {....}
在編譯的過程中,編譯器將dosomething:轉換為一個C函數,它帶有如下的參數列表:(id self,SEL _cmd,int anarg)
每個objective-c類都有一個表,這個表將一個方法的選取器與指向實現該方法的函數的一個指標連結起來。給定了選折器,運行時可以使用這個表來找到匹配的函數的指標。每個類也有一個相關的緩衝機制,這加速了對一個給定方法的後續搜尋。
4,什麼時運行時?
作所有事情的運行時到底是什麼呢?它一點也不神秘。運行時只是普通C函數和結構的一個共用庫。在程式開始運行時,會調用一些這樣的函數。這些函數根據編譯器放置在你的可執行代碼中的資訊來構築表。在執行程式的不同時刻,還會調用其他的函數來尋找並操作這些表中的資訊。這些函數調用都是編譯器自動建立的。
5,向nil發送訊息
按照objective-c的設計,向一個nil接收者發送一條訊息沒有任何效果。當計算一個帶有nil接收者的運算式時,沒有什麼方法會執行。程式默默地忽略nil接收者,而不是引發一個異常或崩潰,這允許我們編寫整潔的代碼。不必像下面這樣搞亂了代碼的結構而保證不會出錯:
Shape*ashape=...
if(ashape!=nil) { [ashape draw]; }
而是可以直接寫成:[ ashape draw ];
如果ashape剛好是nil,就不會繪製什麼內容,並且,從下一行代碼開始繼續執行。如果訊息運算式中方法的傳回值類型是一個指標或數字類型,則發送給nil的一條訊息的傳回值是0。對於其他的傳回型別(例如,結構),在某些情況下傳回值是不確定的,有些地方可能需要使用一個nil接收者來引發一次異常,但是,大多數情況下避免使用if語句將會使代碼更整齊。如果在特定的情況下,捕獲一個nil接收者很重要,那麼你也可以去測試它。
6,選取器
一條訊息的方法名部分有時候叫做選取器或方法選折器,因為,運行時使用它來選擇要執行哪一個方法。選取器只是名稱,他們不帶有任何類型的資訊。Objective-c定義了一個類型,SEL,來儲存選取器的表示。SEL與選取器名稱有一對一的關係,但是,一個SEL自身並非一個字串。帶有相同名稱的所有選取器,具有相同的SEL,並且,不同名稱的總是對應不同SEL。在內部,為了提高效率,Objective-c使用SEL類型來表示方法。使用字串將會很慢,因為僅僅測試兩個字串是否相同,就需要遍曆字串的所有字元。
7,具有相同名稱的方法
如果使用動態類型(變數類型為id),則具有相同名稱的所有方法應該擁有同樣的參數類型和傳回值,即使他們由不相關的型別宣告。如果違反了這一規則將會得到編譯器的警告;如果忽略這一警告你的程式可能會有非常細微的bug。