caregory (類別)
不論一個類設計的有多完美,在需求的演繹中總會碰到無法預測的情況,可以動態的爲已有的類添加新行爲
一、caregate主要作用
1 > 爲已知類添加方法
2 > 可以把類的實現部分放在幾個不同的文件裏,減少單個文件的體積;可以把不同功能組織到不同的Category中;可以由多個開發者共同完成一個類
二、Category實現原理
所有OC類和對象在runtime底層都是以結構體表示的,是category_t
typedef 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; 添加的所有屬性
} category_t;
可以看出category可以添加實例方法、類方法、實現協議、添加屬性、但不能添加實例變量。這裏簡單說一下實例變量、屬性之間的關係
@interface MyViewController :UIViewControlle
{
UIButton *yourButton;
int count;
id data;
}
@property (nonatomic, strong) UIButton *myButton;
@end
實例變量:由類定義的,除去基本數據類型 int、float,,,其他類型的變量都是實例變量
成員變量:代碼中的變量、包括實例變量和基本變量類型,不需要和外界接觸,默認的是protected,一般非子類都無法訪問
屬性:編譯器自動將變量的setter和getter 方法的合成,可以用點語法來讀取,可以爲變量使用,可以與外界接觸
實例變量 + 基本數據類型 = 成員變量
在{ }中聲明的都是成員變量,所以youButton、count、data都是成員變量,實例變量是針對類而言的,成員變量用於類的內部,成員變量不會生成setter和getter方法,爲了方便就有了屬性,屬性的好處是允許其他對象訪問到該變量,因爲屬性的創建自動生成了setter和getter方法
如下:如果聲明瞭實例變量就會報錯
oc的運行時依賴於runtime,加載過程
拿到編譯器爲我們準備的category的數組,將category的實例方法、協議、屬性添加到類上,把類方法協議方法加到,metaclass上,(category的各種列表添加到類上會調用 addUnattachedCategoryForClass,但他只是一個映射,真正處理事情的是re'methodizeClass,對於添加類方法會調用 attachCategoryMethods方法,他只有把所有的Category實例拼成一個大的實例方法交給attachMethodList方法),然而Category的方法並沒有替換原來的方法,會覆蓋原來的方法,因爲Category的方法被放到了新列表的前面,在運行時查找方法的時候是順着方法列表的順序查找的,只要查找到了對應的名字方法,就會調用
爲什麼Category中 不能添加實例變量?
1、前邊說過,Category本身的定義中沒有實例變量的數組,但在runtime中確實有一個函數:class_addIvar().用來給類添加實例變量,但是這個函數只能在構建一個雷的過程中調用,一旦類完成了定義,就不能再添加成員變量了,類在經過編譯期之後就啓動了runtime,就沒有機會調用addIvar方法了,程序在運行時動態的構建類會調用objc_allocateClasspair之後,但在objc_registerClass之前纔可以被調用,同樣是沒有機會。
2、一塊區域內存包含了所有的成員變量和isa指針,因此在編譯的時候就已經分配好了內存,所以動態的改變了成員變量就改變了這塊內存的佈局
那麼如果一定要才Category中實現添加變量
OC是一門動態的語言,runtime中有兩個函數objc_getAssoctatedObject / objc_setAssoctatdObject 來訪問和生成關聯對象,這兩個方法可以讓一個對象和另一個對象關聯,也就是說一個對象保持對另一個對象的引用,並獲取那個對象
定義一個屬性,重寫setter和getter方法
在setter和getter方法中返回 return objc_getAssoctatedObject(self,name) 和 objc_setAssoctatedObject(self,nameKey,name,now)
extension 和 Category 比較
extension看起來像一個匿名的Category,但是extension和有名字的Category是兩個不同的東西,extension在編譯期決定,他就是類的一部分,在編譯期將頭文件中的interface和實現裏邊的implement一起形成一個完整的類,一般用來隱藏隱藏類的私有信息,必須要有一個類的源碼才能添加,它伴隨着這個類的產生而產生,消失而消失,因此他可以添加實例變量、屬性、方法,但都是私有方法。
Category是在運行期決定的,他是無法添加實例變量的,在編譯期對象的內存分佈已經確定,如果添加實例變量會破壞類的內存佈局。