天天看點

分類和擴充

這世上,沒有誰活得比誰容易,隻是有人在呼天搶地,有人在默默努力。

分類(Category)

一. 概念

  分類(Category)是OC中的特有文法,它是表示一個指向分類的結構體的指針。原則上它隻能增加方法,不能增加成員(執行個體)變量。具體原因看源碼組成:

Category
Category 是表示一個指向分類的結構體的指針,其定義如下:
typedef struct objc_category *Category;
struct objc_category {
  char *category_name                          OBJC2_UNAVAILABLE; // 分類名
  char *class_name                             OBJC2_UNAVAILABLE; // 分類所屬的類名
  struct objc_method_list *instance_methods    OBJC2_UNAVAILABLE; // 執行個體方法清單
  struct objc_method_list *class_methods       OBJC2_UNAVAILABLE; // 類方法清單
  struct objc_protocol_list *protocols         OBJC2_UNAVAILABLE; // 分類所實作的協定清單
}
           

  通過上面我們可以發現,這個結構體主要包含了分類定義的執行個體方法與類方法,其中instance_methods 清單是 objc_class 中方法清單的一個子集,而class_methods清單是元類方法清單的一個子集。但這個結構體裡面,根本沒有屬性清單。

​ 分類的作用:

  1. Objective-C 中的 Category 就是對裝飾模式的一種具體實作。它的主要作用是在不改變原有類的前提下,動态地給這個類添加一些方法。

​ 分類使用原則:

  1. 分類是用于給原有類添加方法的,因為分類的結構體指針中,沒有屬性清單,隻有方法清單。是以原則上講它隻能添加方法, 不能添加屬性(成員變量),實際上可以通過其它方式添加屬性 ;
  2. 分類中可以寫@property,但不會生成

    setter/getter

    方法,也不會生成私有的成員變量(編譯時會報警告);
  3. 可以在分類中通路原有類中.h中的屬性;
  4. 如果分類中有和原有類同名的方法, 會優先調用分類中的方法,就是說會忽略原有類的方法。是以同名方法調用的優先級為

    分類 > 本類 > 父類

    。是以在開發中盡量不要覆寫原有類;
  5. 如果多個分類中都有和原有類中同名的方法,那麼調用該方法的時候執行誰由編譯器決定;編譯器會執行最後一個參與編譯的分類中的方法。

二. 分類的定義

  1. 分類格式:
    @interface 待擴充的類(分類的名稱)
    @end
    
    @implementation 待擴充的名稱(分類的名稱)
    @end
               
  2. 執行個體代碼:
    // XBCar + MinCar.h檔案
    @interface XBCar (MinCar)
    
    @property (nonatomic,copy)NSString *nameWithSetterGetter;  //設定setter/getter方法的屬性
    @property (nonatomic,copy)NSString *nameWithoutSetterGetter;  //不設定setter/getter方法的屬性(注意是可以寫在這,而且編譯隻會報警告,運作不報錯)
    
    - (void)minCarCategoryMethod; //分類方法
    
    @end		
               
  3. 分類中@property相關研究

      結合上面分析我們知道,分類中原則上是不能添加屬性的,那麼為什麼我們添加了運作的時候不會報錯呢?既然分類不讓添加屬性,為什麼仍然能編譯通過呢?

      首頁我們應該了解分類不能添加屬性的實質原因是什麼?我們知道在一個類中用@property聲明屬性,編譯器會自動幫我們生成

    _成員變量

    setter/getter

    方法,但分類的指針結構體中,根本沒有屬性清單。是以在分類中用@property聲明屬性,既無法生成

    _成員變量

    也無法生成

    setter/getter

    。是以結論是:我們可以用@property聲明屬性,編譯和運作都會通過,隻要不使用程式也不會崩潰。但如果調用了

    _成員變量

    setter/getter

    方法,報錯就在所難免了。
    #import <objc/runtime.h>
    
    static NSString *nameWithSetterGetterKey = @"nameWithSetterGetterKey"; //定義一個key值
    
    @implementation XBCar (MinCar)
    
    // 運作時實作setter方法
    - (void)setNameWithSetterGetter:(NSString *)nameWithSetterGetter {
      	objc_setAssociatedObject(self, &nameWithSetterGetterKey, nameWithSetterGetter, 	OBJC_ASSOCIATION_COPY);
    }
    
    // 運作時實作getter方法
    - (NSString *)getNameWithSetterGetter {
      	return objc_getAssociatedObject(self, &nameWithSetterGetterKey);
    }
    
    @end
               

擴充(Extension)

二. 擴充的定義

@interface XXX ()
//私有屬性
//私有方法(如果不實作,編譯時會報警,Method definition for 'XXX' not found)
@end
           

三. 作用

  1. 為一個類添加額外的、原來沒有的私有變量,私有方法和私有屬性;
  2. 隻以聲明存在,寄生于宿主類.m中(一般的類擴充寫到

    .m

    檔案中,一般的私有屬性寫到

    .m

    檔案中的類擴充中。);

類别和類擴充的差別

  1. 類别原則上隻能增加方法(能添加屬性的的原因隻是通過

    runtime

    解決無

    setter/getter

    的問題而已);
  2. 類擴充不僅可以增加方法,還可以增加執行個體變量(或者屬性),隻是該執行個體變量預設是@private類型的(

    用範圍隻能在自身類,而不是子類或其他地方);

  3. 類擴充中聲明的方法沒被實作,編譯器會報警,但是類别中的方法沒被實作編譯器是不會有任何警告的。這是因為類擴充是在編譯階段被添加到類中,而類别是在運作時添加到類中;
  4. 類擴充不能像類别那樣擁有獨立的實作部分(@implementation部分),也就是說,類擴充所聲明的方法必須依托對應類的實作部分來實作;
  5. 定義在 .m 檔案中的類擴充方法為私有的,定義在 .h 檔案(頭檔案)中的類擴充方法為公有的。類擴充是在 .m 檔案中聲明私有方法的非常好的方式;

繼續閱讀