1. 程式人生 >實用技巧 >其它 >Category與Extension

Category與Extension


技術標籤:iOS開發iosobjective-c

1. Category

1.1 定義與特點

  • ObjC語言的一個靈活的類擴充套件機制,可以在不知道原有程式碼的基礎上向一個已存在類新增新的方法
  • 不能修改、刪除已有方法,對擴充套件開放、對修改封閉,體現了面向物件的OCP原則,降低了程式碼的耦合度。
  • 類別擴充套件的新方法有更高優先順序,會覆蓋類中已有的同名方法;宣告的方法可以不實現,但不實現時不可以呼叫此方法

1.2 關於新增變數

  • 只能新增擴充套件方法,不能新增成員變數
  • 因為Category是執行期決議,執行時物件的記憶體佈局已經確定,objc_class結構體大小已確定,無法再新增成員變數
  • 在Category中定義了property後,編譯器只為其聲明瞭存取方法,但沒有實現;也沒有合成加下劃線的例項變數名,導致無法訪問例項變數名

1.3 用runtime為屬性新增存取方法

  1. 可以使用runtime提供的關聯物件方法,動態的為property實現getter和setter,以實現setter為例
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
  • key是一個void指標
  • value是關聯物件,是id型別(編譯時型別不確定,視為任意Object型別)
  • policy指定一個記憶體管理策略來處理關聯物件,
    OBJC_ASSOCIATION_ASSIGN:被關聯物件釋放時,關聯物件不會被釋放
    OBJC_ASSOCIATION_COPY、OBJC_ASSOCIATION_COPY:關聯物件會被釋放
  1. 擴充套件NSString類的方法為例,擴充套件屬性、例項方法和類方法,使用runtime實現屬性的getter和setter,程式碼如下:
//  NSString+Category.h

#import <Foundation/Foundation.h>

@interface NSString (Category) {
    /*不可新增成員變數*/
}
@property(nonatomic,copy) NSString *newString;
//擴充套件的例項方法和類方法
+(void)categoryClassMethod;
-(void)categoryInstanceMethod;
@end
//  NSString+Category.m

#import "NSString+Category.h"
#import <objc/runtime.h>

@implementation NSString (Category)

//runtime強行實現newString的getter和setter
-(NSString *)newString {
    return objc_getAssociatedObject(self, @"newString");
}

-(void)setNewString:(NSString *)newString {
    objc_setAssociatedObject(self, @"newString", newString, OBJC_ASSOCIATION_COPY);
}

//擴充套件的例項方法和類方法
+(void)categoryClassMethod {
    NSLog(@"這是擴充套件的類方法");
}
-(void)categoryInstanceMethod {
    NSLog(@"這是擴充套件的例項方法");
}
@end
  • 在主函式中呼叫
//  main.m

#import <Foundation/Foundation.h>
#import "NSString+Category.h"

int main(int argc, const char * argv[]) {
    NSString *string = [[NSString alloc] init];
    
    //runtime實現的getter和setter
    string.newString = @"你好";
    NSLog(@"%@",string.newString);
    
    //擴充套件的類方法和例項方法
    [NSString categoryClassMethod];
    [string categoryInstanceMethod];
    
    return 0;
}
  • 執行結果如下

Category與Extension

2. Extension

2.1 位置

  • 在類的.m檔案中@implementation之前的地方,即類的continuous區域

2.2 為啥要用

  • 類擴充套件的作用本來是私有方法的前向宣告,但最新的編譯器可以在.m檔案中任意位置寫實現而無需宣告,如果聲明瞭就一定要實現
  • 現在Extension區域的作用主要是定義類的私有屬性,將暴露給外部的屬性、變數和方法定義在.h檔案中,不想暴露的直接定義在.m檔案的Extension區
  • 私有並不是絕對的,仍然可以使用KVC和runtime暴力訪問私有屬性和變數、使用runtime暴力訪問私有方法

2.2 關於新增變數

  • Extension是編譯器決議的,它就是類的一部分,因此可以新增成員變數
  • Category執行期決議,不能新增成員變數
/*類擴充套件區域*/
@interface extensionClass()

/* 類擴充套件屬性,類外部不可訪問*/
@property(nonatomic, copy) NSString* extensionVariable;

/* 類擴充套件方法宣告,可省略,若聲明瞭就得在後面實現 */
-(void)extensionInstanceMethod;
+(void)extensionClassMethod;