runtime 方法介绍

Objective-C Runtime

Runtime

Working with Classes

  1. class_getName
    返回类的名字
const char  * name=class_getName([Person class]);
NSLog(@"name=%s",name);
NSString *userName=[NSString stringWithUTF8String:name];
NSLog(@"userName=%@",userName);

  1. class_getSuperclass
    返回父类的类的类型(class)
Class aclass = class_getSuperclass([Student class]);
    const char * name = class_getName(aclass);
    NSLog(@"name=%s",name);
    NSString *userName=[NSString stringWithUTF8String:name];
    NSLog(@"userName=%@",userName);

  1. class_isMetaClass
    返回该类是否是元类
BOOL isMeta = class_isMetaClass([NSObject class]);
  1. class_getInstanceSize
    返回该类实例的大小
size_t size = class_getInstanceSize([Person class]);
    NSLog(@"%zu",size);
  1. class_getInstanceVariable
    返回实例变量的Ivar
Ivar ivar = class_getInstanceVariable([Person class],"_name");
    const char * ivar_name=ivar_getName(ivar);
    NSLog(@"ivar_name = %@",[NSString stringWithUTF8String:ivar_name]);
  1. class_addIvar
    给类添加成员变量
  • 该方法不能给已经存在的类添加成员变量
  • 该方法只能在 objc_allocateClassPair方法之后,objc_registerClassPair这个方法之前调用
BOOL class_addIvar(Class cls, const char *name, size_t size, uint8_t alignment, const char *types);
Class People = objc_allocateClassPair([NSObject class], "People", 0);
    
class_addIvar(People, "_name", sizeof(NSString *), 0, @encode(NSString *));
class_addIvar(People, "_age", sizeof(NSNumber *), 0, @encode(NSNumber *));

objc_registerClassPair(People);
  1. class_addProperty
    给类添加属性
BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount);
objc_property_attribute_t attribute1 = {"T", @encode(NSString *)};
objc_property_attribute_t attribute2 = {"N", ""};
objc_property_attribute_t attribute3 = {"&", ""};
objc_property_attribute_t attribute[] = {attribute1, attribute2, attribute3};
class_addProperty([HCBase class], "sb", attribute, 3);
  1. class_addMethod
    给已经存在的类添加实例方法
_aperson =[[Person alloc]init];
    BOOL sucess = class_addMethod([Person class],(NSSelectorFromString(@"methodName")),class_getMethodImplementation([self class], @selector(printtext:)), "v@:@");
    if(sucess){
        [_aperson performSelector:(NSSelectorFromString(@"methodName")) withObject:@"hahahhahah" afterDelay:0];
    }
  1. class_addProtocol
    给一个已有的类添加协议
BOOL class_addProtocol(Class cls, Protocol *protocol);
BOOL sucess = class_addProtocol([Person class],_test);
  1. class_copyIvarList
    获取一个类的成员变量列表的一个拷贝
Ivar  _Nonnull * class_copyIvarList(Class cls, unsigned int *outCount);
unsigned int count;
    Ivar * ivars =  class_copyIvarList([Person class], &count);
    for (int i=0; i<count; i++) {
        NSLog(@"-----%@",[NSString stringWithUTF8String:ivar_getName(ivars[i])]);
    }
    free(ivars);
  • outcount为成员变量的个数
  • 只会显示当前类的成员变量,不包括父类的任何成员变量
  • 你必须使用free()方法释放ivars数组
  1. class_getIvarLayout
    获取类所有的strong修饰符数组
const uint8_t * class_getIvarLayout(Class cls);
const uint8_t *array_s = class_getIvarLayout([Person class]);
   int i = 0;
   uint8_t value_s = array_s[i];
   while (value_s != 0x0) {
       printf("\\x%02x\n", value_s);
       value_s = array_s[++i];
   }
  1. class_setIvarLayout
    设置Ivar 属性修饰符
void class_setIvarLayout(Class cls, const uint8_t *layout);
  1. class_getWeakIvarLayout
    获取类所有的weak修饰符数组
const uint8_t * class_getWeakIvarLayout(Class cls);
const uint8_t *array_s1 = class_getWeakIvarLayout([Person class]);
    int j = 0;
    uint8_t value_s1 = array_s1[j];
    while (value_s1 != 0x0) {
        printf("\\x%02x\n", value_s1);
        value_s1 = array_s1[++j];
    }
  1. class_setWeakIvarLayout
    设置Ivar weak修饰符
void class_setWeakIvarLayout(Class cls, const uint8_t *layout);
  1. class_getProperty
    获取指定属性
objc_property_t class_getProperty(Class cls, const char *name);
objc_property_t  propert_t = class_getProperty([Person class],"name");
    const char * properName = property_getName(propert_t);
    NSLog(@"--%@",[NSString stringWithUTF8String:properName]);
  1. class_copyPropertyList
    获取一个类的属性列表的一个拷贝
objc_property_t  _Nonnull * class_copyPropertyList(Class cls, unsigned int *outCount);
unsigned int count;
    objc_property_t *array = class_copyPropertyList([Person class], &count);
    for (int i=0; i<count; i++) {
        const char * name = property_getName(array[i]);
        NSLog(@"---%@",[NSString stringWithUTF8String:name]);
    }
  1. class_getInstanceMethod
    获取类中的指定实例的 Method
Method class_getInstanceMethod(Class cls, SEL name);
Method method = class_getInstanceMethod([Person class], @selector(printName));
    NSLog(@"---%@",[NSString stringWithUTF8String:(method_getName(method))]);
  1. class_getClassMethod
    获取类中指定的类的Method
Method class_getClassMethod(Class cls, SEL name);
  1. class_copyMethodList
    获取当前类的所有实例方法的拷贝
Method  _Nonnull * class_copyMethodList(Class cls, unsigned int *outCount);
unsigned int count;
    Method *methods = class_copyMethodList([Person class], &count);
    for (int i = 0; i < count; i++) {
        const char *methodName =sel_getName((method_getName(methods[i])));
        NSLog(@"---methodname=%@",[NSString stringWithUTF8String:methodName]);
    }
  1. class_replaceMethod
    替换类中方法的实现地址
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types);
Method originalMethod = class_getInstanceMethod([Person class], @selector(printName));
    Method swizzledMethod = class_getInstanceMethod([Person class], @selector(printAge));
    class_replaceMethod([Person class],
                        @selector(printAge),
                        method_getImplementation(originalMethod),
                        method_getTypeEncoding(originalMethod));
    Person *person = [[Person alloc]init];
    [person printAge];
  • 返回替换后旧的IMP
  • types类型可以使用该方法获取:method_getTypeEncoding()
  • 如果要替换的方法不存在,首先需要使用class_addMethod添加方法,然后在去替换
  1. class_getMethodImplementation
    获取类中指定方法的方法指针
IMP class_getMethodImplementation(Class cls, SEL name);
 IMP methodImp = class_getMethodImplementation([Person class], @selector(printName));
    NSLog(@"%ld",(long)methodImp);
  1. class_respondsToSelector
    是否类的实例能够响应该方法
BOOL class_respondsToSelector(Class cls, SEL sel);

可使用下列方法替换:

  • respondsToSelector:
  • instancesRespondToSelector:
  1. class_replaceProperty
    替换类中的一个属性
void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount);
- (void)class_replaceProperty:(Class)class class_name:(const char *)class_name replace_name:(const char *)replace_name{
    objc_property_attribute_t type = { "T", "@\"NSString\"" };
    objc_property_attribute_t ownership = { "C", "" }; // C = copy
    objc_property_attribute_t backingivar  = { "V", replace_name };
    objc_property_attribute_t attrs[] = { type, ownership, backingivar };
    class_replaceProperty(class, class_name, attrs, 3);
}
  1. class_conformsToProtocol
    判断该类是否遵循协议
BOOL class_conformsToProtocol(Class cls, Protocol *protocol);

可用NSObject里边的conformsToProtocol:替换

Adding Classes

  1. objc_allocateClassPair
    创建一个新的类和元类
Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes);

extraBytes参数通常为0

  • 实例变量及实例方法添加到类中
  • 类方法添加至元类中
  1. objc_disposeClassPair
    销毁一个类及它的的元类
void objc_disposeClassPair(Class cls);

当该类的实例及子类存在时,不能调用该方法

  1. objc_registerClassPair
    注册一个类
void objc_registerClassPair(Class cls);

Instantiating Classes

  1. class_createInstance
    创建一个类的实例,且默认分配内存空间
id class_createInstance(Class cls, size_t extraBytes);
  1. objc_constructInstance
    幢一个类的实例,且分配实例的字节大小
id objc_constructInstance(Class cls, void *bytes);
  1. objc_destructInstance
    销毁了一个类的实例没有释放内存并删除任何相关的引用
void * objc_destructInstance(id obj);

Working with Instances

  1. object_copy
    返回一个对象的拷贝
id object_copy(id obj, size_t size);
  1. object_dispose
    释放一个对象的内存
id object_dispose(id obj);
  1. object_setInstanceVariable
    修改一个类的实例的变量值
Ivar object_setInstanceVariable(id obj, const char *name, void *value)
Person *person =[[Person alloc]init];
    NSString * string = @"xiao hong";
    Ivar ivar = object_setInstanceVariable(person,"_name",&string);
    [person printName];

ARC环境下不可用

  1. object_getInstanceVariable
    获取一个实例的变量值
Ivar object_getInstanceVariable(id obj, const char *name, void * _Nullable *outValue);
Person *person =[[Person alloc]init];
    NSString *name;
    object_getInstanceVariable(person,"_name",&name);
    NSLog(@"name = %@",name);

ARC环境下不可用

  1. object_getClassName
    获取一个对象的类名
const char * object_getClassName(id obj);
  1. object_getClass
    返回一个对象的Class
Class object_getClass(id obj);
Person *person =[[Person alloc]init];
    Class aclass = object_getClass(person);
    NSLog(@"---aclass = %@",[NSString stringWithUTF8String:(class_getName(aclass))]);
  1. object_setClass
    修改实例的类 返回之前的类的Class
Class object_setClass(id obj, Class cls);
Person *person =[[Person alloc]init];
    Class aclass = object_setClass(person, [Student class]);
    NSLog(@"---aclass = %@",[NSString stringWithUTF8String:(class_getName(aclass))]);
    NSLog(@"person = %@",[NSString stringWithUTF8String:object_getClassName(person)]);

Working with Instance Variables

  1. ivar_getName
    返回变量的名字
const char * ivar_getName(Ivar v);
  1. ivar_getTypeEncoding
    返回string类型的变量类型
const char * ivar_getTypeEncoding(Ivar v);
  1. ivar_getOffset
    返回变量的偏移量
ptrdiff_t ivar_getOffset(Ivar v);

Associative References

  1. objc_setAssociatedObject
    给一个对象添加协议键值
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
objc_setAssociatedObject(self,&flashColorKey, flashColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

给分类添加协议属性时用到

  1. objc_getAssociatedObject
    获取对象的协议值
id objc_getAssociatedObject(id object, const void *key);

给分类添加协议属性时用到

  1. objc_removeAssociatedObjects
    移除对象的所有协议
void objc_removeAssociatedObjects(id object);

一般通过将value设置成nil来移除协议关系,如果用该方法会影响别的协议

Sending Messages

  1. objc_msgSend
id objc_msgSend(id self, SEL op, ...);
Person *person =[[Person alloc]init];
    [person printName];
    ((void (*) (id, SEL)) objc_msgSend)(person, sel_registerName("printName"));
    NSLog(@"---------%@",[person getPrintName:@"yangguoqiang"]);
    NSString *name=((NSString * (*) (id, SEL,NSString *)) objc_msgSend)(person, sel_registerName("getPrintName:"),@"yangguoqiang");
    NSLog(@"---------%@",name);
    dog *adog = [person getDogWithAge:10 andLevel:1];
    NSLog(@"---%@",[NSString stringWithUTF8String:object_getClassName(adog)]);
    dog *twoDog = ((dog * (*) (id, SEL,NSInteger,NSInteger)) objc_msgSend)(person, sel_registerName("getDogWithAge:andLevel:"),10,1);
    NSLog(@"---%@",[NSString stringWithUTF8String:object_getClassName(twoDog)]);

Working with Methods

  1. method_invoke
    方法调用
id method_invoke(id receiver, Method m, ...);
Person *person =[[Person alloc]init];
    [person printName];
    Method method = class_getInstanceMethod([Person class], @selector(printName));
    method_invoke(person,method);
  1. method_invoke_stret
    方法调用返回一个结构体
void method_invoke_stret(id receiver, Method m, ...);
  1. method_getName
    通过一个Method类型返回SEL类型
SEL method_getName(Method m);
Person *person =[[Person alloc]init];
    [person printName];
    Method method = class_getInstanceMethod([Person class], @selector(printName));
    SEL seletor = method_getName(method);
    NSLog(@"----%@",[NSString stringWithUTF8String:sel_getName(seletor)]);
  1. method_getImplementation
    通过Method对象获取方法的IMP地址
IMP method_getImplementation(Method m);
  1. method_getTypeEncoding
    通过Method的对象返回一个字符串,描述方法的参数及返回值
const char * method_getTypeEncoding(Method m);
Method method = class_getInstanceMethod([Person class], @selector(printName));
    NSLog(@"----%@",[NSString stringWithUTF8String:method_getTypeEncoding(method)]);
  1. method_copyReturnType
    通过Method的对象拷贝返回值的字符串 要使用free()方法释放
char * method_copyReturnType(Method m);
 Method method = class_getInstanceMethod([Person class], @selector(getPrintName:));
    NSLog(@"----%@",[NSString stringWithUTF8String:method_copyReturnType(method)]);
  1. method_copyArgumentType
    按index返回字符串参数的类型 要使用free()方法释放
char * method_copyArgumentType(Method m, unsigned int index);
Method method = class_getInstanceMethod([Person class], @selector(getPrintName:));
    NSLog(@"----%@",[NSString stringWithUTF8String:method_copyArgumentType(method,0)]);
  1. method_getNumberOfArguments
    返回参数的个数
unsigned int method_getNumberOfArguments(Method m);
  1. method_setImplementation
    给方法设置新的实现地址 返回旧的方法地址
IMP method_setImplementation(Method m, IMP imp);
  1. method_exchangeImplementations
    交互两个方法的实现地址
void method_exchangeImplementations(Method m1, Method m2);
Person *person =[[Person alloc]init];
    [person printName];
    Method method1 = class_getInstanceMethod([Person class], @selector(printName));
    Method method2 = class_getInstanceMethod([Person class], @selector(printAge));
    method_exchangeImplementations(method1,method2);
    [person printName];

Working with Libraries

  1. objc_copyImageNames
    获取所有的Objective-C 动态库及静态库名称的字符串数组
const char * _Nonnull * objc_copyImageNames(unsigned int *outCount);
unsigned int count;
    const char ** names = objc_copyImageNames(&count);
    for (int i= 0; i<count; i++) {
        NSLog(@"-----%@",[NSString stringWithUTF8String:names[i]]);
    }
  1. class_getImageName
    返回一个字符串路径,显示该类属于哪个库
const char * class_getImageName(Class cls);
const char * name = class_getImageName([Person class]);
    NSLog(@"-----%@",[NSString stringWithUTF8String:name]);
  1. objc_copyClassNamesForImage
    获取指点库中所有类的字符数组名
const char * _Nonnull * objc_copyClassNamesForImage(const char *image, unsigned int *outCount);

Working with Selectors

  1. sel_getName
    获取SEL类型方法的字符串名称
const char * sel_getName(SEL sel);
  1. sel_registerName
    在runtime系统中注册方法
SEL sel_registerName(const char *str);
  1. sel_getUid
    在runtime系统中注册方法
SEL sel_getUid(const char *str);
  1. sel_isEqual
    判断两个方法是不是相等
BOOL sel_isEqual(SEL lhs, SEL rhs);

Working with Protocols

  1. objc_getProtocol
    Returns a specified protocol.
  2. objc_copyProtocolList
    Returns an array of all the protocols known to the runtime.
  3. objc_allocateProtocol
    Creates a new protocol instance.
  4. objc_registerProtocol
    Registers a newly created protocol with the Objective-C runtime.
  5. protocol_addMethodDescription
    Adds a method to a protocol.
  6. protocol_addProtocol
    Adds a registered protocol to another protocol that is under construction.

Working with Properties

  1. property_getName
    返回属性的字符名称
const char * property_getName(objc_property_t property);
  1. property_getAttributes
    返回属性的字符
Returns the attribute string of a property.
  1. property_copyAttributeValue
    Returns the value of a property attribute given the attribute name.
char * property_copyAttributeValue(objc_property_t property, const char *attributeName);
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352

推荐阅读更多精彩内容