iOS類新增方法、屬性學習筆記

躍然發表於2015-03-03

一、在執行時為類新增方法

我們首先定義了一個EmptyClass,繼承NSObject,沒有任何自帶方法,接著定義了一個函式。這裡提一句,Obj-C的方法(method)就是一個至少需要兩個引數(self,_cmd)的C函式,這個函式僅僅輸出一句Hello。接下來在addMethod方法中,我們呼叫class_addMethod()為EmptyClass新增方法,class_addMethod()是這樣定義的:

BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)

引數說明:

cls:被新增方法的類。

name:可以理解為方法名,這個貌似隨便起名,比如我們這裡叫sayHello2。

imp:實現這個方法的函式。

types:一個定義該函式返回值型別和引數型別的字串。

接著建立EmptyClass的例項,呼叫sayHello2,執行,輸出Hello,新增方法成功。

#if TARGET_IPHONE_SIMULATOR
#import <objc/objc-runtime.h>
#else
#import <objc/runtime.h>
#import <objc/message.h>
#endif

@interface EmptyClass:NSObject

@end

@implementation EmptyClass

@end

void sayHello(id self, SEL _cmd) {
    NSLog(@"Hello");
}

- (void)addMethod {
    class_addMethod([EmptyClass class], @selector(sayHello2), (IMP)sayHello, "v@:");

    // Test Method
    EmptyClass *instance = [[EmptyClass alloc] init];
    [instance sayHello2];

    [instance release];

}

二、為什麼不要重寫原有類方法

主要原因如下:

CocoaFramework有很多是用Category實現的,重寫之後,會導致在Runtime的時 候,只有一個方法會被執行,而哪個會被執行是undefined。

例如,重寫NSString的一個方法base64EncodedString,而其他CocoaFramework的也有可能使用Category來實現這個方法,這樣就會導致在Runtime的時候,執行哪個方法是Undefined。

另外,有個地方要注意,那就是Category方法的命名。
通常的明明方式是加一個字首,例如

-(void)WC_setUpCustomButton;

因為,現在名字並未發生衝突,IOS版本在更新,SDK也在更新,也就是說要儘量保證將來方法的名稱也不發生衝突。

三、聯合儲存實現方式及底層原理解析

動態語言的最大好處,就是靈活性,對於Objective-C來說,能在執行時動態地為類增加方法和例項變數是很多其它語言羨慕不已的能力。現在說說為類增加例項變數用到的技術:聯合儲存。

一、聯合儲存的實現方式
下面這段程式碼實現了為Duck類增加color屬性:
Duck+associative.h檔案

#import "Duck.h"  

@interface Duck (associative)  

@property (nonatomic, retain) NSString *color;  

@end 

Duck+associative.m檔案

#import "Duck+associative.h"  
#import <objc/runtime.h>  

@implementation Duck (associative)  

static char colorKey = NULL;  

- (NSString *)color {  
    return objc_getAssociatedObject(self, &colorKey);  
}  

- (void)setColor:(NSString *)aColor {  
    objc_setAssociatedObject(self, &colorKey,  
                             aColor,  
                             OBJC_ASSOCIATION_RETAIN);  
}  

呼叫舉例:

Duck    *smallDuck = [[Duck alloc] init];  
smallDuck.color = @"red color";  
NSLog(@"duck color:%@",smallDuck.color);  
[smallDuck release];  

輸出結果:

2013-07-18 19:09:26.578 ObjcRunTime[429:403] duck color:red color  

至此,我們已經成功的為Duck類增加了一個color屬性。

四、聯合儲存的優缺點

1、優點
聯合儲存的最大的優點,在於它能通過靈活的方式實現為類增加屬性。

2、缺點
效率低,使用單條機器指令就可以訪問真正的例項變數,但是訪問儲存在對映表中的值需要多個函式呼叫,效率問題還是需要注意的。

事實上,目前許多Cocoa類,像NSAttributedString、NSFileManager、NSNotification、NSProcessInfo等都廣泛地使用了聯合儲存。

參考連結:

  1. iOS在執行時為類新增方法
  2. Objective-C通過聯合儲存為類增加屬性及原理解析
  3. iOS - 如何給1個類新增屬性
  4. Add variables to an existing class in objective-c
  5. Faking instance variables in Objective-C categories with Associative References

相關文章