iOS Runtime 運(yùn)行時(二) 類

#import "ViewController.h"
#import <objc/runtime.h>
@interface ViewController ()
@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self runtimeDemo];
}
void cMethod(id self, SEL _cmd, NSString *string){
    NSLog(@"add C IMP 調(diào)用%@", string);
}
- (void)ocMethod:(NSString *)string{
    NSLog(@"add OC IMP 調(diào)用%@", string);
}
@end

1、類 Class

- (void)runtimeDemo{
    
    Class testClass = self.class;
    
    //1、獲取類名 如果cls是Nil 返回空字符串
    const char * className = class_getName(testClass);
    NSLog(@"1、%s",className);
    
    //2、獲取父類,如果cls是根類 or Nil 返回Nil.
    Class superClass = class_getSuperclass(testClass);
    NSLog(@"2、%@",superClass);
    
    //在以后的文章中介紹元類
    //3、獲取指定類的元類,返回元類名,如果runtime沒注冊則返回nil
    Class metaClass =  objc_getMetaClass(className);
    NSLog(@"3、%@",metaClass);
    
    //4、判斷是不是元類
    BOOL isMeta =  class_isMetaClass(metaClass);
    NSLog(@"4、%d",isMeta);
}

控制臺

2017-11-09 16:54:55.754991+0800 runtime[8224:2271947] 1、ViewController
2017-11-09 16:54:55.755202+0800 runtime[8224:2271947] 2、UIViewController
2017-11-09 16:54:55.755321+0800 runtime[8224:2271947] 3、ViewController
2017-11-09 16:54:55.755433+0800 runtime[8224:2271947] 4、1
2、創(chuàng)建類,并且添加方法與變量

通過調(diào)用object_getClass(newClass),您可以獲得一個指向新元類的指針。要創(chuàng)建一個新類,首先調(diào)用objc_allocateClassPair。然后使用class_addMethod和class_addIvar等函數(shù)設(shè)置類的屬性。在構(gòu)建類時,調(diào)用objc_registerClassPair。新class現(xiàn)在可以使用了。實(shí)例方法和實(shí)例變量應(yīng)該添加到類本身。類方法應(yīng)該添加到元類。

(1)添加一個新類和元類

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

class_addMethod會重寫父類的實(shí)現(xiàn),但不會替換當(dāng)前類中已有的實(shí)現(xiàn)。要更改現(xiàn)有的實(shí)現(xiàn),請使用method_setImplementation。

   /**
     *superClass: 父類,傳Nil會創(chuàng)建一個新的根類
     *name: 類名
     *extraBytes: 0
     *return:返回新類,創(chuàng)建失敗返回Nil,如果類名已經(jīng)存在,則創(chuàng)建失敗
     */
    Class runTimeClass = objc_allocateClassPair([UIViewController class], "runTimeClass", 0);

    /**
     *添加成員變量
     *這個函數(shù)只能在objc_allocateClassPair和objc_registerClassPair之前調(diào)用。不支持向現(xiàn)有類添加一個實(shí)例變量。
     *這個類不能是元類。不支持在元類中添加一個實(shí)例變量。
     *實(shí)例變量的最小對齊為1 << align。實(shí)例變量的最小對齊依賴于ivar的類型和機(jī)器架構(gòu)。對于任何指針類型的變量,請通過log2(sizeof(pointer_type))。
     */
    BOOL isAddSuccess1 = class_addIvar(runTimeClass, "_name", sizeof(NSString*), log2(sizeof(NSString*)), @encode(NSString*));
    BOOL isAddSuccess2 = class_addIvar(runTimeClass, "_dataArr", sizeof(NSMutableArray*), log2(sizeof(NSMutableArray*)), @encode(NSMutableArray*));

    //添加對象方法
    //types 一組字符,用來描述方法的參數(shù)的類型。由于函數(shù)必須至少使用兩個arguments - self和_cmd,因此第二個和第三個字符必須是 “@:” (第一個字符是返回類型)。
 
    class_addMethod(runTimeClass, NSSelectorFromString(@"dynamicCMethod"), (IMP)cMethod, "v@:@");
    //獲取oc方法的指針
    IMP ocIMP = class_getMethodImplementation_stret([self class], @selector(ocMethod:));
    //動態(tài)添加一個oc方法的實(shí)現(xiàn)
    class_addMethod(runTimeClass, NSSelectorFromString(@"dynamicOCMethod"),ocIMP , "v@:@");
    objc_registerClassPair(runTimeClass);

    //添加一個類方法
    //獲取runTimeClass的元類
    Class metaClass =  objc_getMetaClass(class_getName(runTimeClass));
    class_addMethod(metaClass, NSSelectorFromString(@"dynamicClassMethod"), ocIMP, "v@:@");
    
    //copy成員變量列表
    //返回Ivar指針類型的數(shù)組
    unsigned int count = 0;
    Ivar * varList =  class_copyIvarList(runTimeClass, &count);
    // 遍歷成員變量列表,其中每個變量都是Ivar類型的結(jié)構(gòu)體
    //const Ivar *p = varList 初始化一個指針,指向數(shù)組的地址
    for (const Ivar *p = varList; p < varList + count; p++)
    {
        //用ivar接收指針p指向的地址 存儲的內(nèi)容
        Ivar const ivar = *p;
        NSLog(@"查看成員變量 ivar: %@", [NSString stringWithUTF8String:ivar_getName(ivar)]);
    }
    // 用完之后釋放內(nèi)存
    free(varList);
    
    //使用動態(tài)創(chuàng)建的類,變量與方法
    id  test = [[runTimeClass alloc] init];
    //給_dataArr賦值
    [test setValue:@[@1,@2] forKey:@"_dataArr"];
    NSLog(@"_dataArr的值\n%@",[test valueForKey:@"_dataArr"]);
    //調(diào)用動態(tài)添加的方法
    [test performSelector:NSSelectorFromString(@"dynamicCMethod") withObject:@"動態(tài)添加的C方法" afterDelay:0];
    [test performSelector:NSSelectorFromString(@"dynamicOCMethod") withObject:@"動態(tài)添加的OC方法" afterDelay:0];
    //調(diào)用類方法
    //注意要使用類進(jìn)行調(diào)用
    [runTimeClass performSelector:NSSelectorFromString(@"dynamicClassMethod") withObject:@"動態(tài)添加的類方法" afterDelay:0];
2017-11-13 20:57:54.578454+0800 runtime[48091:4961752] 查看成員變量 ivar: _name
2017-11-13 20:57:54.578614+0800 runtime[48091:4961752] 查看成員變量 ivar: _dataArr
2017-11-13 20:57:54.578972+0800 runtime[48091:4961752] _dataArr的值
(
    1,
    2
)
2017-11-13 20:57:54.586745+0800 runtime[48091:4961752] add C IMP 調(diào)用動態(tài)添加的C方法
2017-11-13 20:57:58.478024+0800 runtime[48091:4961752] add OC IMP 調(diào)用動態(tài)添加的OC方法
2017-11-13 20:57:58.478409+0800 runtime[48091:4961752] add OC IMP 調(diào)用動態(tài)添加的類方法

(2)銷毀類以及關(guān)聯(lián)的元類
被銷毀的類類必須是使用objc_allocateClassPair來分配的。
【注意】如果cls類或任何子類的實(shí)例存在,則不要調(diào)用此函數(shù)。

  //先銷毀cls的對象
   test = nil;
   objc_disposeClassPair(runTimeClass);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容