Runtime簡單使用

ios runtime

然后我們打開終端,在命令行找到cd到文件目錄,然后中輸入:

clang -rewrite-objc main.m

命令可以將main.m編譯成C++的代碼,改成不同的文件名,就會生成不同的c++代碼
這是就生成了main.cpp這個c++文件,打開文件代碼
查看該main.cpp最底下的main函數,
這樣我們就可以看到底層具體實現的方式!

這時,我們就需要知道這些方法:

  • objc_msgSend 可以給對象發送消息
  • objc_getClass(“Person”) 可以獲取到指定名稱的對象
  • sel_registerName(“alloc”) 可以調用到對象的方法

通過查看,c++代碼,我們得出結論:

使用objc_msgSend函數,給objc_getClass函數實例化的對象發送sel_registerName獲取到的方法
這么一個消息

增加

增加函數:class_addMethod
增加實例變量:class_addIvar
增加屬性:@dynamic標簽,或者class_addMethod,因為屬性其實就是由getter和setter函數組成
增加Protocol:class_addProtocol 

獲取

獲取函數列表及每個函數的信息(函數指針、函數名等等):class_getClassMethod method_getName ...
獲取屬性列表及每個屬性的信息:class_copyPropertyList property_getName
獲取類本身的信息,如類名等:class_getName class_getInstanceSize
獲取變量列表及變量信息:class_copyIvarList

替換

將實例替換成另一個類:object_setClass
將函數替換成一個函數實現:class_replaceMethod
直接通過char *格式的名稱來修改變量的值,而不是通過變量

其他如下:

1、對象拷貝:id object_copy(id obj, size_t size)

- (void) copyObj
{
    CustomClass *obj = [CustomClassnew];
    NSLog(@"%p", &obj);
    
    id objTest = object_copy(obj,sizeof(obj));
    NSLog(@"%p", &objTest);

    [objTest fun1];
}
打印結果:
2013-07-26 15:35:11.547 HighOC[6859:c07] 0xbfffdf64
2013-07-26 15:35:11.547 HighOC[6859:c07] 0xbfffdf60
2013-07-26 15:35:11.547 HighOC[6859:c07] fun1

說明:
object_copy 函數實現了對象的拷貝。

2、對象釋放 id object_dispose(id obj)

- (void) objectDispose
{
    CustomClass *obj = [CustomClassnew];
    object_dispose(obj);
    
    [obj release];
    [obj fun1];
}
打印結果:程序crash
malloc: *** error for object 0x758e6d0: pointer being freed was not allocated

3、更改對象的類/獲取對象的類

Class object_setClass(id obj, Class cls) / Class object_getClass(id obj)

- (void) setClassTest
{
    CustomClass *obj = [CustomClassnew];
    [obj fun1];
    
    Class aClass =object_setClass(obj, [CustomClassOtherclass]);
    //obj 對象的類被更改了    swap the isa to an isa
    NSLog(@"aClass:%@",NSStringFromClass(aClass));
    NSLog(@"obj class:%@",NSStringFromClass([objclass]));
    [obj fun2];
}
- (void) getClassTest
{
    CustomClass *obj = [CustomClassnew];
    Class aLogClass =object_getClass(obj);
    NSLog(@"%@",NSStringFromClass(aLogClass));
}

4、獲取對象的類名 constchar *object_getClassName(id obj)

- (void) getClassName
{
    CustomClass *obj = [CustomClassnew];
    NSString *className = [NSStringstringWithCString:object_getClassName(obj)encoding:NSUTF8StringEncoding];
    NSLog(@"className:%@", className);
}

5、給一個類添加方法

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

/**
  * 一個參數
  */

int cfunction(id self, SEL _cmd, NSString *str) {
    NSLog(@"%@", str);
    return10;//隨便返回個值
}

- (void) oneParam {
    
    TestClass *instance = [[TestClassalloc]init];
    //    方法添加
    class_addMethod([TestClassclass],@selector(ocMethod:), (IMP)cfunction,"i@:@");
    
    if ([instance respondsToSelector:@selector(ocMethod:)]) {
        NSLog(@"Yes, instance respondsToSelector:@selector(ocMethod:)");
    } else
    {
        NSLog(@"Sorry");
    }
    
    int a = (int)[instanceocMethod:@"我是一個OC的method,C函數實現"];
    NSLog(@"a:%d", a);
}

 /**
  * 兩個參數
  */

int cfunctionA(id self, SEL _cmd, NSString *str, NSString *str1) {
    NSLog(@"%@-%@", str, str1);
    return20;//隨便返回個值
}

- (void) twoParam {
    
    TestClass *instance = [[TestClassalloc]init];
    
    class_addMethod([TestClassclass],@selector(ocMethodA::), (IMP)cfunctionA,"i@:@@");
    
    if ([instance respondsToSelector:@selector(ocMethodA::)]) {
        NSLog(@"Yes, instance respondsToSelector:@selector(ocMethodA::)");
    } else
    {
        NSLog(@"Sorry");
    }
    
    int a = (int)[instanceocMethodA:@"我是一個OC的method,C函數實現" :@"-----我是第二個參數"];
    NSLog(@"a:%d", a);
}

相關文檔及說明:

Obj-C的方法(method)就是一個至少需要兩個參數(self,_cmd)的C函數

IMP有點類似函數指針,指向具體的Method實現。

向一個類動態添加方法

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

參數說明:

  • cls:被添加方法的類
  • name:可以理解為方法名
  • imp:實現這個方法的函數
  • types:一個定義該函數返回值類型和參數類型的字符串

class_addMethod([TestClass class], @selector(ocMethod:), (IMP)testFunc, "i@:@");

  • 其中types參數為"i@:@“,按順序分別表示:
  • i:返回值類型int,若是v則表示void
  • @:參數id(self)
  • ::SEL(_cmd)
  • @:id(str)

華麗分割線


一些公用類:

ClassCustomClass.h

@interface ClassCustomClass :NSObject{
    NSString *varTest1;
    NSString *varTest2;
    NSString *varTest3;
}
@property (nonatomic,assign)NSString *varTest1;
@property (nonatomic,assign)NSString *varTest2;
@property (nonatomic,assign)NSString *varTest3;
- (void) fun1;
@end

ClassCustomClass.m

@implementation ClassCustomClass
@synthesize varTest1, varTest2, varTest3;
- (void) fun1 {
    NSLog(@"fun1");
}
@end

ClassCustomClassOther.h

@interface ClassCustomClassOther :NSObject {
    int varTest2;
}
- (void) fun2;
@end

ClassCustomClassOther.m

@implementation ClassCustomClassOther
- (void) fun2 {
    NSLog(@"fun2");
}
@end

ClassPropertyViewCtr.h

@interface ClassPropertyViewCtr () {
    float myFloat;
    ClassCustomClass *allobj;
}
myFloat = 2.34f;

6、獲取一個類的所有方法:

- (void) getClassAllMethod
{
    u_int count;
    Method* methods= class_copyMethodList([UIViewController class], &count);
    for (int i = 0; i < count ; i++)
    {
        SEL name = method_getName(methods[i]);
        NSString *strName = [NSString stringWithCString:sel_getName(name)encoding:NSUTF8StringEncoding];
        NSLog(@"%@",strName);
    }
}

打印結果(部分):

2013-07-26 16:07:03.972 HighOC[7021:c07] _screen
2013-07-26 16:07:03.973 HighOC[7021:c07] applicationWillSuspend
2013-07-26 16:07:03.973 HighOC[7021:c07] _tryBecomeRootViewControllerInWindow:
2013-07-26 16:07:03.973 HighOC[7021:c07] isViewLoaded
2013-07-26 16:07:03.974 HighOC[7021:c07] view
......................

7、獲取一個類的所有屬性:

- (void) propertyNameList
{
    u_int count;
    objc_property_t *properties=class_copyPropertyList([UIViewControllerclass], &count);
    for (int i = 0; i < count ; i++)
    {
        const char* propertyName =property_getName(properties[i]);
        NSString *strName = [NSString stringWithCString:propertyNameencoding:NSUTF8StringEncoding];
        NSLog(@"%@",strName);
    }
}

打印結果(部分)

2013-07-26 16:09:42.182 HighOC[7041:c07] tabBarItem
2013-07-26 16:09:42.184 HighOC[7041:c07] tabBarController
2013-07-26 16:09:42.185 HighOC[7041:c07] splitViewController
2013-07-26 16:09:42.186 HighOC[7041:c07] navigationItem
2013-07-26 16:09:42.186 HighOC[7041:c07] hidesBottomBarWhenPushed
...............

8、獲取/設置類的屬性變量

//獲取全局變量的值   (myFloat 為類的一個屬性變量)
- (void) getInstanceVar {
    float myFloatValue;
    object_getInstanceVariable(self,"myFloat", (void*)&myFloatValue);
    NSLog(@"%f", myFloatValue);
}
//設置全局變量的值
- (void) setInstanceVar {
    float newValue = 10.00f;
    unsigned int addr = (unsignedint)&newValue;
    object_setInstanceVariable(self,"myFloat", *(float**)addr);
    NSLog(@"%f", myFloat);
}

9、判斷類的某個屬性的類型

- (void) getVarType {
    ClassCustomClass *obj = [ClassCustomClassnew];
    Ivar var = class_getInstanceVariable(object_getClass(obj),"varTest1");
    const char* typeEncoding =ivar_getTypeEncoding(var);
    NSString *stringType =  [NSStringstringWithCString:typeEncodingencoding:NSUTF8StringEncoding];
    
    if ([stringType hasPrefix:@"@"]) {
        // handle class case
        NSLog(@"handle class case");
    } else if ([stringTypehasPrefix:@"i"]) {
        // handle int case
        NSLog(@"handle int case");
    } else if ([stringTypehasPrefix:@"f"]) {
        // handle float case
        NSLog(@"handle float case");
    } else
    {
 
    }
}

10、通過屬性的值來獲取其屬性的名字(反射機制)

- (NSString *)nameOfInstance:(id)instance
{
    unsigned int numIvars =0;
    NSString *key=nil;
    
    //Describes the instance variables declared by a class. 
    Ivar * ivars = class_copyIvarList([ClassCustomClassclass], &numIvars);
    
    for(int i = 0; i < numIvars; i++) {
        Ivar thisIvar = ivars[i];
        
        const char *type =ivar_getTypeEncoding(thisIvar);
        NSString *stringType =  [NSStringstringWithCString:typeencoding:NSUTF8StringEncoding];
        
        //不是class就跳過
        if (![stringType hasPrefix:@"@"]) {
            continue;
        }
        
        //Reads the value of an instance variable in an object. object_getIvar這個方法中,當遇到非objective-c對象時,并直接crash
        if ((object_getIvar(allobj, thisIvar) == instance)) {
            // Returns the name of an instance variable.
            key = [NSStringstringWithUTF8String:ivar_getName(thisIvar)];
            break;
        }
    }
    free(ivars);
    return key;
}

測試代碼:

allobj = [ClassCustomClassnew];
allobj.varTest1 =@"varTest1String";
allobj.varTest2 =@"varTest2String";
allobj.varTest3 =@"varTest3String";
NSString *str = [selfnameOfInstance:@"varTest1String"];
NSLog(@"str:%@", str);

打印結果:

2013-07-26 16:26:26.271 HighOC[7081:c07] str:varTest1

11、系統類的方法實現部分替換

- (void) methodExchange {
    Method m1 = class_getInstanceMethod([NSStringclass],@selector(lowercaseString));
    Method m2 = class_getInstanceMethod([NSStringclass],@selector(uppercaseString));
    method_exchangeImplementations(m1, m2);
    NSLog(@"%@", [@"sssAAAAss"lowercaseString]);
    NSLog(@"%@", [@"sssAAAAss"uppercaseString]);
}

打印結果:(仔細看log)

2013-07-26 16:33:22.776 HighOC[7104:c07] SSSAAAASS
2013-07-26 16:33:22.778 HighOC[7104:c07] sssaaaass

12、自定義類的方法實現部分替換

- (void) justLog1 {
    NSLog(@"justLog1");
}
- (void) justLog2 {
    NSLog(@"justLog2");
}
- (void) methodSetImplementation {
    Method method = class_getInstanceMethod([ClassMethodViewCtrclass],@selector(justLog1));
    IMP originalImp = method_getImplementation(method);
    Method m1 = class_getInstanceMethod([ClassMethodViewCtrclass],@selector(justLog2));
    method_setImplementation(m1, originalImp);
}
//[self methodSetImplementation];
//[self justLog2];

13、覆蓋系統方法

IMP cFuncPointer;
IMP cFuncPointer1;
IMP cFuncPointer2;

NSString* CustomUppercaseString(idself,SEL_cmd){
    printf("真正起作用的是本函數CustomUppercaseString\r\n");
    NSString *string = cFuncPointer(self,_cmd);
    return string;
}
NSArray* CustomComponentsSeparatedByString(idself,SEL_cmd,NSString *str){
    printf("真正起作用的是本函數CustomIsEqualToString\r\n");
    return cFuncPointer1(self,_cmd, str);
}
//不起作用,求解釋
bool CustomIsEqualToString(idself,SEL_cmd,NSString *str) {
    printf("真正起作用的是本函數CustomIsEqualToString\r\n");
    return cFuncPointer2(self,_cmd, str);
}
- (void) replaceMethod{
    cFuncPointer = [NSStringinstanceMethodForSelector:@selector(uppercaseString)];
    class_replaceMethod([NSStringclass],@selector(uppercaseString), (IMP)CustomUppercaseString,"@@:");
    cFuncPointer1 = [NSStringinstanceMethodForSelector:@selector(componentsSeparatedByString:)];
    class_replaceMethod([NSStringclass],@selector(componentsSeparatedByString:), (IMP)CustomComponentsSeparatedByString,"@@:@");
    cFuncPointer2 = [NSStringinstanceMethodForSelector:@selector(isEqualToString:)];
    class_replaceMethod([NSStringclass],@selector(isEqualToString:), (IMP)CustomIsEqualToString,"B@:@");
}

14、自動序列化(轉)

 #import "NSObject+AutoEncodeDecode.h"
@implementation NSObject (AutoEncodeDecode)
- (void)encodeWithCoder:(NSCoder *)encoder {
    Class cls = [selfclass];
    while (cls != [NSObjectclass]) {
        unsigned int numberOfIvars =0;
        Ivar* ivars = class_copyIvarList(cls, &numberOfIvars);
        for(const Ivar* p = ivars; p < ivars+numberOfIvars; p++){
            Ivar const ivar = *p;
            const char *type =ivar_getTypeEncoding(ivar);
            NSString *key = [NSStringstringWithUTF8String:ivar_getName(ivar)];
            id value = [selfvalueForKey:key];
            if (value) {
                switch (type[0]) {
                    case _C_STRUCT_B: {
                        NSUInteger ivarSize =0;
                        NSUInteger ivarAlignment =0;
                        NSGetSizeAndAlignment(type, &ivarSize, &ivarAlignment);
                        NSData *data = [NSDatadataWithBytes:(constchar *)self + ivar_getOffset(ivar)
                                                      length:ivarSize];
                        [encoder encodeObject:dataforKey:key];
                    }
                        break;
                    default:
                        [encoder encodeObject:value
                                       forKey:key];
                        break;
                }
            }
        }
        free(ivars);
        cls = class_getSuperclass(cls);
    }
}

- (id)initWithCoder:(NSCoder *)decoder {
    self = [self init];
    
    if (self) {
        Class cls = [selfclass];
        while (cls != [NSObjectclass]) {
            unsigned int numberOfIvars =0;
            Ivar* ivars = class_copyIvarList(cls, &numberOfIvars);
            
            for(constIvar* p = ivars; p < ivars+numberOfIvars; p++){
                Ivar const ivar = *p;
                const char *type =ivar_getTypeEncoding(ivar);
                NSString *key = [NSStringstringWithUTF8String:ivar_getName(ivar)];
                id value = [decoder decodeObjectForKey:key];
                if (value) {
                    switch (type[0]) {
                        case _C_STRUCT_B: {
                            NSUInteger ivarSize =0;
                            NSUInteger ivarAlignment =0;
                            NSGetSizeAndAlignment(type, &ivarSize, &ivarAlignment);
                            NSData *data = [decoderdecodeObjectForKey:key];
                            char *sourceIvarLocation = (char*)self+ivar_getOffset(ivar);
                            [data getBytes:sourceIvarLocationlength:ivarSize];
                        }
                            break;
                        default:
                            [self setValue:[decoder decodeObjectForKey:key]
                                    forKey:key];
                            break;
                    }
                }
            }
            free(ivars);
            cls = class_getSuperclass(cls);
        }
    }
    
    return self;
}

用C代替OC:

 #import <objc/runtime.h> 
 #import <objc/message.h>
 #import <stdio.h>

extern int UIApplicationMain (int argc,char *argv[],void *principalClassName,void *delegateClassName);


struct Rect {
  float x;
  float y;
  float width;
  float height;
};
typedef struct Rect Rect;


void *navController;
static int numberOfRows =100;



int tableView_numberOfRowsInSection(void *receiver,structobjc_selector *selector, void *tblview,int section) {
  returnnumberOfRows;
}

void *tableView_cellForRowAtIndexPath(void *receiver,structobjc_selector *selector, void *tblview,void *indexPath) {
  Class TableViewCell = (Class)objc_getClass("UITableViewCell");
  void *cell = class_createInstance(TableViewCell,0);
  objc_msgSend(cell, sel_registerName("init"));
  char buffer[7];
  int row = (int) objc_msgSend(indexPath, sel_registerName("row"));
  sprintf (buffer, "Row %d", row);
  void *label =objc_msgSend(objc_getClass("NSString"),sel_registerName("stringWithUTF8String:"),buffer);
  objc_msgSend(cell, sel_registerName("setText:"),label);
  return cell;
}

void tableView_didSelectRowAtIndexPath(void *receiver,structobjc_selector *selector, void *tblview,void *indexPath) {
  Class ViewController = (Class)objc_getClass("UIViewController");
  void * vc = class_createInstance(ViewController,0);
  objc_msgSend(vc, sel_registerName("init"));
  char buffer[8];
  int row = (int) objc_msgSend(indexPath, sel_registerName("row"));
  sprintf (buffer, "Item %d", row);
  void *label =objc_msgSend(objc_getClass("NSString"),sel_registerName("stringWithUTF8String:"),buffer);
  objc_msgSend(vc, sel_registerName("setTitle:"),label);
  objc_msgSend(navController,sel_registerName("pushViewController:animated:"),vc,1);
}

void *createDataSource() {
  Class superclass = (Class)objc_getClass("NSObject");
  Class DataSource = objc_allocateClassPair(superclass,"DataSource",0);
  class_addMethod(DataSource,sel_registerName("tableView:numberOfRowsInSection:"), (void(*))tableView_numberOfRowsInSection,nil);
  class_addMethod(DataSource,sel_registerName("tableView:cellForRowAtIndexPath:"), (void(*))tableView_cellForRowAtIndexPath,nil);
  objc_registerClassPair(DataSource);
  returnclass_createInstance(DataSource,0);
}

void * createDelegate() {
  Class superclass = (Class)objc_getClass("NSObject");
  Class DataSource = objc_allocateClassPair(superclass,"Delegate",0);
  class_addMethod(DataSource,sel_registerName("tableView:didSelectRowAtIndexPath:"), (void(*))tableView_didSelectRowAtIndexPath,nil);
  objc_registerClassPair(DataSource);
  returnclass_createInstance(DataSource,0);
}



void applicationdidFinishLaunching(void *receiver,structobjc_selector *selector, void *application) {
  Class windowClass = (Class)objc_getClass("UIWindow");
  void * windowInstance = class_createInstance(windowClass, 0);
    
  objc_msgSend(windowInstance, sel_registerName("initWithFrame:"),(Rect){0,0,320,480});
  
  //Make Key and Visiable
  objc_msgSend(windowInstance,sel_registerName("makeKeyAndVisible"));

  //Create Table View
  Class TableViewController = (Class)objc_getClass("UITableViewController");
  void *tableViewController = class_createInstance(TableViewController, 0);
  objc_msgSend(tableViewController, sel_registerName("init"));
  void *tableView = objc_msgSend(tableViewController,sel_registerName("tableView"));
  objc_msgSend(tableView, sel_registerName("setDataSource:"),createDataSource());
  objc_msgSend(tableView, sel_registerName("setDelegate:"),createDelegate());
 
  Class NavController = (Class)objc_getClass("UINavigationController");
  navController = class_createInstance(NavController,0);
  objc_msgSend(navController,sel_registerName("initWithRootViewController:"),tableViewController);
  void *view =objc_msgSend(navController,sel_registerName("view"));
  
  //Add Table View To Window
  objc_msgSend(windowInstance, sel_registerName("addSubview:"),view);
}


//Create an class named "AppDelegate", and return it's name as an instance of class NSString
void *createAppDelegate() {
  Class mySubclass = objc_allocateClassPair((Class)objc_getClass("NSObject"),"AppDelegate",0);
  structobjc_selector *selName =sel_registerName("application:didFinishLaunchingWithOptions:");
  class_addMethod(mySubclass, selName, (void(*))applicationdidFinishLaunching,nil);
  objc_registerClassPair(mySubclass);
  returnobjc_msgSend(objc_getClass("NSString"),sel_registerName("stringWithUTF8String:"),"AppDelegate");
}


int main(int argc, char *argv[]) {
  returnUIApplicationMain(argc, argv,0,createAppDelegate());
}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 轉至元數據結尾創建: 董瀟偉,最新修改于: 十二月 23, 2016 轉至元數據起始第一章:isa和Class一....
    40c0490e5268閱讀 1,776評論 0 9
  • 原文出處:南峰子的技術博客 Objective-C語言是一門動態語言,它將很多靜態語言在編譯和鏈接時期做的事放到了...
    _燴面_閱讀 1,258評論 1 5
  • 我們常常會聽說 Objective-C 是一門動態語言,那么這個「動態」表現在哪呢?我想最主要的表現就是 Obje...
    Ethan_Struggle閱讀 2,232評論 0 7
  • Objective-C語言是一門動態語言,他將很多靜態語言在編譯和鏈接時期做的事情放到了運行時來處理。這種動態語言...
    tigger丨閱讀 1,439評論 0 8
  • 繼上Runtime梳理(四) 通過前面的學習,我們了解到Objective-C的動態特性:Objective-C不...
    小名一峰閱讀 770評論 0 3