Category
是Objective-C 2.0
以後添加的語言特性,它的主要做用是爲已經存在的類添加方法,通常稱爲分類。 Category
在iOS
開發中使用很是的頻繁,特別是在爲系統類進行拓展的時候,咱們能夠不用繼承系統類,直接給系統類添加方法,最大程度的體現了Objective-C
的動態語言特性。數組
新建了一個工程,建立了一個實體類,和一個category分類 bash
cmd+B
編譯以後打開終端,cd到工程的目錄,執行
clang -rewrite-objc Test+categroy.m
命令
Test+categroy.cpp
文件
_category_t
,看到編譯出來的category結構體
struct category_t {
const char *name;
classref_t cls;
struct method_list_t *instanceMethods; // 對象方法
struct method_list_t *classMethods; // 類方法
struct protocol_list_t *protocols; // 協議
struct property_list_t *instanceProperties; // 屬性
// Fields below this point are not always present on disk.
struct property_list_t *_classProperties;
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
複製代碼
能夠看出,在源碼中Category
的底層結構category_t
和上文中咱們獲得的_category_t
結構體基本一致,在編譯以後,每一個Category
確實會生成一個category_t
類型的結構體。並且category_t
類型的結構體中是沒有ivars
這項,這就是分類是不能添加成員變量的緣由。數據結構
dyld
是蘋果的動態加載器,用來加載image
(image
指的是Mach-O
格式的二進制文件,不是圖片)dyld
,而dyld
會將咱們APP所依賴的各類庫加載到內存中,其中就包括libobjc
庫(OC
和runtime
),這些工做,是在APP
的main
函數執行以前完成的_objc_init
是Object-C runtime
的入口函數,在這裏主要是讀取Mach-O
文件OC
對應的Segment section
,並根據其中的數據代碼信息,完成爲OC
的內存佈局,以及初始化runtime
相關的數據結構。_objc_init
函數void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
environ_init();
tls_init();
static_init();
lock_init();
exception_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
複製代碼
_dyld_objc_notify_register
這一句代碼,這裏註冊了三個函數&map_images
將image
加載進內存load_imagesdyld
初始化加載image
方法unmap_images
移除內存 下面探究map_images
這個函數void map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
{
mutex_locker_t lock(runtimeLock);
return map_images_nolock(count, paths, mhdrs);
}
複製代碼
map_images_nolock
函數是用來執行全部類註冊和修復操做,並調用+load方法
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
...
//前面的代碼省略,主要代碼是_read_images這個函數
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
firstTime = NO;
}
複製代碼
_read_images
函數中有多個段落,咱們研究的重點在於分類// Discover categories.
for (EACH_HEADER) {
// 獲取項目中全部的Category,獲得一個二維數組
category_t **catlist =
_getObjc2CategoryList(hi, &count);
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
// 遍歷二維數組,獲得每個category_t類型的結構體變量
for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
// 經過cat->cls拿到當前Category所屬的類
Class cls = remapClass(cat->cls);
if (!cls) {
catlist[i] = nil;
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
"missing weak-linked target class",
cat->name, cat);
}
continue;
}
// 判斷class是否存在
bool classExists = NO;
// 若是Category中存在實例方法,協議或者是實例屬性
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
// 將cls的未合併的全部Category存放到以cls爲key的一個映射表中去
addUnattachedCategoryForClass(cat, cls, hi);
if (cls->isRealized()) {
// 從新組織實例方法,將Category中的方法、屬性、協議等等附加到cls的實例方法列表、實例屬性列表和協議列表中去
remethodizeClass(cls);
classExists = YES;
}
if (PrintConnecting) {
_objc_inform("CLASS: found category -%s(%s) %s",
cls->nameForLogging(), cat->name,
classExists ? "on existing class" : "");
}
}
// 若是Category中存在類方法,協議或者是類屬性
if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties))
{
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
// 從新組織元類方法,將Category中的類方法、類屬性、協議等等附加到cls的元類的類方法列表、類屬性列表和協議列表中去
if (cls->ISA()->isRealized()) {
remethodizeClass(cls->ISA());
}
if (PrintConnecting) {
_objc_inform("CLASS: found category +%s(%s)",
cls->nameForLogging(), cat->name);
}
}
}
}
複製代碼
remethodizeClass
函數,重點函數在attachCategories
static void remethodizeClass(Class cls)
{
category_list *cats;
bool isMeta;
runtimeLock.assertWriting();
isMeta = cls->isMetaClass();
// Re-methodizing: check for more categories
if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {
if (PrintConnecting) {
_objc_inform("CLASS: attaching categories to class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
// 把分類信息附加到類/元類中
attachCategories(cls, cats, true /*flush caches*/);
free(cats);
}
}
複製代碼
attachCategories
函數static void
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
if (!cats) return;
if (PrintReplacedMethods) printReplacements(cls, cats);
// 判斷傳入的類是否爲元類
bool isMeta = cls->isMetaClass();
// fixme rearrange to remove these intermediate allocations
// 建立一個存放方法的二維數組
method_list_t **mlists = (method_list_t **)
malloc(cats->count * sizeof(*mlists));
// 建立一個存放屬性的二維數組
property_list_t **proplists = (property_list_t **)
malloc(cats->count * sizeof(*proplists));
// 建立一個存放協議的二維數組
protocol_list_t **protolists = (protocol_list_t **)
malloc(cats->count * sizeof(*protolists));
// Count backwards through cats to get newest categories first
int mcount = 0;
int propcount = 0;
int protocount = 0;
int i = cats->count;
bool fromBundle = NO;
while (i--) {
// 取出其中一個分類
auto& entry = cats->list[i];
// 取出分類中的方法存放到二維數組 mlists 中,isMeta決定是類方法仍是實例方法
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
mlists[mcount++] = mlist;
fromBundle |= entry.hi->isBundle();
}
// 取出分類中的屬性存放到二維數組 proplists 中
property_list_t *proplist =
entry.cat->propertiesForMeta(isMeta, entry.hi);
if (proplist) {
proplists[propcount++] = proplist;
}
// 取出分類中的協議存放到二維數組 protolists 中
protocol_list_t *protolist = entry.cat->protocols;
if (protolist) {
protolists[protocount++] = protolist;
}
}
// 獲取到類對象中的數據
auto rw = cls->data();
prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
// 將全部分類的對象方法附加到類對象方法列表中
rw->methods.attachLists(mlists, mcount);
free(mlists);
if (flush_caches && mcount > 0) flushCaches(cls);
// 將全部分類的屬性附加到類對象屬性列表中
rw->properties.attachLists(proplists, propcount);
free(proplists);
// 將全部分類的協議附加到類對象協議列表中
rw->protocols.attachLists(protolists, protocount);
free(protolists);
}
複製代碼
attachLists
這個函數主要是關聯原類方法的void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// many lists -> many lists
uint32_t oldCount = array()->count;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
array()->count = newCount;
// 內存移動
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
// 內存拷貝
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
複製代碼