ObjC中Category的原理簡析

2021-09-11 15:40:22 字數 2512 閱讀 4859

objective-c類別也叫分類,是一種不需要繼承即可給類新增方法的語法技術。

通常我們是用category為乙個類新增一些方法。我們可以直接用類似物件對方法呼叫的樣子直接對category中的方法進行呼叫。比如下面的例子,為person(person類定義在.h和.m檔案中了,沒有給出)類定義了乙個名為test的category。

呼叫category中的test方法與例項直接呼叫例項方法一樣,也是通過訊息傳送機制(objc_sendmethod)進行呼叫。

我們都知道類似這種方法呼叫是通過例項的isa指標查詢類物件,在類物件中查詢到相應的例項方法進行呼叫的,當然還包括superclass查詢父類,這裡就不做贅述了。因為通過isa指標查詢方法已經在往深處看-objc物件中有過介紹。

那麼category定義的方法存放在**呢?其實它們跟類的例項物件一樣,也是存放在類物件中。同樣,category中定義的類方法,也是存放在元類物件中的。只不過將category中的資訊合併到類或元類物件中的操作是在執行時完成的而非編譯的時。

使用xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc mian.m -o main.cpp將上述main.m轉換成main.cpp檔案,可以看到person的category (test) 被轉換成了下面這樣乙個_category_t型別的結構體。

檢視_category_t結構體的結構:其中存放有:名稱、類資訊、例項方法和類方法列表、協議列表和屬性列表。

我們可以看到在_objc_$_category_person_$_test中,傳入的值:

上面說過category中的方法、屬性、協議是在執行時合併到類中的,所以我們在執行時的入口objc_os.mm檔案中找到_objc_init方法。我們可以看到在方法上面有注釋表示這是入口函式,會通過dyid去載入一些模組。

順著這個思路我們檢視map_image是通過map_images_nolock(count, paths, mhdrs)獲得的,在這個方法中存在乙個載入模組的方法。

載入模組的函式中有乙個重排方法的函式,在這個函式中有乙個attachcategories(cls, cats, true /*flush caches*/);函式。這個函式是實現附加分類的作用。引數分別傳遞了cls物件和category列表。

attachcategories()中將category中的屬性、方法和協議取出放到陣列中,然後用cls取出類中的class_rw_t結構體,取出的methods、properties、protocols分別呼叫attachlists方法,並且將方法陣列、屬性陣列、協議陣列和陣列的中元素的個數傳遞到attachlists中。

在attachlists函式中將之前存放方法、屬性、協議的陣列擴容至oldcount+addedcount大小。將原方法、屬性、協議的陣列從大陣列的第乙個位置移動到最後的位置,然後將attachcategories傳入的列表拷貝到大陣列的前面,完成category中的方法、屬性、協議向類物件或元類物件的合併。

將category中的內容合併到類或元類在runtime中的操作順序:

首先類或元類方法列表是乙個二維陣列:

runtime合併category資訊到類或元類物件中在runtime原始碼中的函式用軌跡:

attachlists圖示:

我們可以看到在沒有將category的方法列表加到類或元類物件中之前,陣列中只有這乙個列表,而合併之後,它們就被放到了類或元類物件的方法列表的最後面,所以我們呼叫在category中重寫的類中的例項方法或者類方法都會優先執行category中的方法,因為isa在類物件或者元類物件中尋找方法的時候會首先在category中找到,既然找到了就不會繼續往下找了。

但是假如有多個擴充套件都重寫了類的某個方法,首先必然是執行某個category的方法,這個category在方法列表的最前面,那麼多個category的方法列表在類或元類的方法列表中的順序是怎麼決定的呢?

我們看到attachlists中擴充套件中的方法、屬性和協議列表是在attachcategories中生成的,生成的過程:

通過i--來進行乙個while迴圈,其中i是int i = cats->count;即cats的個數。而mlist、proplists和protolists是進行++操作的,操作的結果造成了先取出cats列表最後乙個放到mlist,propcounts的最前面。cats是按照編譯順序排列的,所以後編譯的cat中取出的方法、屬性和協議列表,分別放在mlist、proplists和protolists的最前面。

所以isa指標也會按照上述順序查詢方法,即後編譯的cats中的方法會先呼叫。

**注:**編譯順序可以在xcode中檢視和改變

Objc中block的實現

閉包 閉包是乙個函式 或者是指向函式的指標 再加上函式執行上下文的變數 有時候也稱做自由變數 block 實際上就是 oc語言對閉包的實現。block的資料結構定義如下 isaflags 用bit位 表示一些block的附加描述資訊 reserved 保留變數 invoke 函式指標 指向具體的bl...

ObjC中isEqual與 的區別

isequal是nsobject的方法,我們常用它來判斷兩個object是否相等,而 也常用來來判斷兩個object是否相等,那麼它們有什麼區別呢。看一段 就明白了 inte ce myitem nsobject property nonatomic,copy nsstring identifier...

objc 中的布林值

1.bool bool,在c語言中是沒有定義的,objective c中有bool是因為它使用的編譯器能識別這樣的資料型別,被解釋為int型。2.bool bool,在objc中是用來做真假判斷的,多用於物件。3.boolean boolean 是乙個舊的carbon 關鍵字,他的資料型別是unsi...