ios之Runtime的使用

ios开发Runtime的简单使用方法,ios开发runtime

ios开发Runtime的简单使用方法,其实在OC底层本质上就是C语言函数的调用,去动态的发送消息。下面就拿一个alloc
init的方法来举个例子,我们完全可以去调用运行时的函数,这样就可以去创建一个对象了。

   Person * p = objc_msgSend(objc_getClass("Person"),sel_registerName("alloc"));

  objc_msgSend(p, sel_registerName("init"));

其实我们在终端也可以去输入下面的命令,就可以去查看OC的C语言的实现,就会发现其实创建对象就和上面的运行时代码是很像的

clang -rewrite-objc main.m

在这里也需要了解两个概念性的问题就是SEL和IMP,其实SEL就是方法的编号,而IMP就是需要去执行方法的指针,我们可以通过方法的编号去找到这个代码的具体实现。

我们在下面可以进行交换两个对象的方法,其中Method就是指向struct
objc_method的指针。其实SEL和IMP就相当于是一本书里面的标题和页码,我们可以通过标题知道页码,当然也可以去根据页码知道具体的内容。

    Method method1 = class_getInstanceMethod([self class], @selector(eat));
    Method method2 = class_getInstanceMethod([self class], @selector(sleep));
    //进行方法交换
    method_exchangeImplementations(method1, method2);

有两个方法是我们需要知道的一个就是当我们没有实现某个我们调用的类方法的时候回来到下面的这个函数

+(BOOL)resolveClassMethod:(SEL)sel

另外一个就是在我们实现我们调用的对象方法的时候如果没找到会调用下面的这个函数

+(BOOL)resolveInstanceMethod:(SEL)sel

如果我们想要在运行时动态的添加函数就可以在这里面进行添加,并且下面需要注意的是如果我们要写的函数要带有参数的话,那么函数的前面就要带有两个参数,一个是self,还有一个就是SEL
_cmd,当前_cmd你可以改名字。self其实就是方法的调用者,而SEL其实就是方法的编号

+(BOOL)resolveInstanceMethod:(SEL)sel
{

    //动态的添加方法
    /*方法的参数一:是这个类
     参数二:方法
     参数三 函数指针
     参数四 就是返回值的类型加参数
     */

    class_addMethod(self, sel,hello,"");

    return [super resolveInstanceMethod:sel];
}
void hello(id self,SEL _cmd,NSString *str1,NSString * str2)
{
    NSLog(@"%@------%@",str1,str2);
    NSLog(@"hello world");
}

还有一个需要注意的是死循环可能不会导致程序直接崩溃,但是如果是函数的递归调用就肯定会导致程序的崩溃的,因为函数执行的时候有自己的临时栈,所以会导致堆栈溢出了,导致程序崩溃。

ios开发Runtime的简单使用方法,其实在OC底层本质上就是C语言函数的调用,去动态的发送消息。下…

最近正好项目不忙所以没事就研究啦下runtime纯粹是为了装逼,现在我来把我了解到的来介绍下。自己也正好重温下。

iOS开发之Runtime机制深入解析,ios开发runtime机制

本篇主要讲述在 OC 开发中主要涉及到的运行时机制:

以前去面试,经常看到面试要求有runtime,特意去简单学习了一下,在此记录一下方便以后的温习,或许也可以给别人一些经验。

Runtime根据字面意思就是运行时,它是一套纯c语言的API。而oc正好是一门运行时语言,那么运行时语言到底是什么鬼呢?

大家都知道runtime可以字典转模型吧!我来介绍下具体事这样一个原理

运行时的工作:

 运行时在 OC 中的工作:OC
语言的设计模式决定了尽可能的把程序从编译和链接时推迟到运行时。只要有可能,OC
总是使用动态的方式来解决问题。这意味着 OC
语言不仅需要一个编译器,同时也需要一个运行时系统来执行编译好的代码。这儿的运行时系统扮演的角色类似于
OC 语言的操作系统,OC 基于该系统来工作。

 

什么是runtime?

runtime 是 OC底层的一套C语言的API(引入 <objc/runtime.h> 或<objc/message.h>),编译器最终都会将OC代码转化为运行时代码,通过终端命令编译.m 文件:clang -rewrite-objc xxx.m可以看到编译后的xxx.cpp(C++文件)。

1 运行时语言:尽可能的将编译时要执行的逻辑推到了运行时。
a: 最常见的就是我们说的将数据类型的确定由编译推到了运行时。
b:
某个对象使用某个方法,到底使用什么方法在编译时是不确定的,要到运行时才能确定下来。这也就是为什么C语言如果调用未实现的函数在编译的时候就崩溃,但是OC语言会到运行的时候才会崩溃。

1.
先建一个
@interface moldel : NSObject
//@property(nonatomic,assign)int age;
@property(nonatomic,copy)NSString *name;
//@property(nonatomic,assign)double height;
- (void)run;
@end
/* 获取所有成员变量*/
 Ivar *ivars = class_copyIvarList([moldel class], &count);
//通过runtime来获取成员变量的名字和类型
for (int i = 0; i<count; i++) {
        Ivar ivar = ivars[i];
       const char *name = ivar_getName(ivar);
      const char * type = ivar_getTypeEncoding(ivar);
        ivar_getTypeEncoding(ivar);
}
free(ivars);//释放
//这样不是就可以的得到名字来嘛 然后在与对应的网络请求下来的key
moldel *pdd = [[moldel alloc]init];
 NSString *str = [NSString stringWithUTF8String:name];

        if ([str isEqualToString:@"_name"]) {

            pdd.name = @"_name";
     [pdd setValue:_dict[@"_name"]forKeyPath:@"_name"];
        }
2.交换方法
有这样一个场景我给数组传nil程序会崩溃这样传统的方法是:需要每加一次我们就需要判读一次这样肯定影响效率

写一个分类
#import "NSMutableArray+lsnsmutable.h"
#import <objc/runtime.h>
@implementation NSMutableArray (lsnsmutable)
+(void)load{

    Method othermehtod = class_getInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(hm_addobject:));
    Method originmehtod = class_getInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(addObject:));
    //交换方法
    method_exchangeImplementations(othermehtod, originmehtod);

}
//减号方法用class_getInstanceMethod
- (void)hm_addobject:(id)object{
//    [object isKindOfClass:[NSString class]];//表示字符串
    if (object != nil) {
        [self hm_addobject:object];
    }


}

怎样就好啦 不用每次都去判断一次  还有其它就自己去扩展啦
还有 就是+load我就不多说了 自己去百度

 运行时的简单应用:

 OC 2.0运行时系统参考库描述了OC
运行库的数据结构和函数接口。程序可以通过这些接口来和 OC
运行时系统交互。例如:增加一个类或者方法,或者获得所有类的定义列表等。

 

一、交换两个方法的实现,拦截系统自带的方法调用功能

需要用到的方法 <objc/runtime.h>

1.获得某个类的类方法

Method class_getClassMethod(Class cls , SEL name)

2.获得某个类的实例对象方法

Method class_getInstanceMethod(Class cls , SEL name)

3.交换两个方法的实现

void method_exchangeImplementations(Method m1 , Method m2)

2 Runtime的使用场景

 运行时的两个版本:

 OC 运行时系统有两个版本,早期版本主要应用于 OC1.0中,现行版本用于
OC2.0中,在早期版本中,如果改变了类中实例变量的布局,就必须重新编译该类的所有子类。在现行版本中,如果改变了类中实例变量的布局,无需重新编译该类的任何子类。早起版本一般用于
Max OS X 系统中32位程序,此外可视为全部是现行版本。

 

 

案例1:方法简单的交换

交换类的方法,此处简单写了把系统的UIView的setBackgroundColor的方法换成了自定义的pb_setBackgroundColor。

 1.首先需要导入系统头文件#import <objc/runtime.h>
 2. 创建一个分类Category,实现方法。(点击右键--New File...--iOS--Objective-C File)。Ps:其中File是文件名,File Type可以选择是否选用分类或者拓展等,Class是选择哪一种 类。

在控制器中首先写一个view,设置背景颜色为红色,发现view的背景色为红色。
创建一个分类,在.m里实现,如下:

#import "UIView+BlackView.h"
#import <objc/runtime.h>

@implementation UIView (BlackView)
+(void)load{

    /** 获取原始setBackgroundColor方法 */
    Method originalM = class_getInstanceMethod([self class], @selector(setBackgroundColor:));

    /** 获取自定义的pb_setBackgroundColor方法 */
    Method exchangeM = class_getInstanceMethod([self class], @selector(zdy_setBackgroundColor:));

    /** 交换方法 */
    method_exchangeImplementations(originalM, exchangeM);
}

/** 自定义的方法 */
-(void)zdy_setBackgroundColor:(UIColor *) color{

    NSLog(@"%s",__FUNCTION__);

    /**
     1.更改颜色
     2.所有继承自UIView的控件,设置背景色都会设置成自定义的'orangeColor'
     3. 此时调用的方法 'pb_setBackgroundColor' 相当于调用系统的 'setBackgroundColor' 方法,原因是在load方法中进行了方法交换.
     4. 注意:此处并没有递归操作.
     */
    [self zdy_setBackgroundColor:[UIColor orangeColor]];
}

然后在控制其中加入头文件,发现view的颜色已变成橙色。

a : 获取到类的所有实例变量(公有+私有)等等
 class_copyIvarList()
b : 获取到类的所有方法
class_copyMethodList()
c : 为分类添加属性
objc_setAssociatedObject()+objc_getAssociatedObject()
d : 实现方法交换
class_getInstanceMethod()+method_exchangeImplementations()
e : 实现字典和模型之间的互相转换
class_copyIvarList()

 交互途径:

    1、通过 OC 源代码:

        当编译 OC
类和方法时,编译器为实现语句动态特性将自动创建一些数据结构和函数。运行时系统的主要功能就是根据源代码中的表达式发送消息。

 

    2、通过 Foundation 框架中类 NSObject 的方法:

        Cocoa 程序中绝大部分类都是 NSObject 类的子类,所以大部分都继承了
NSObject 类的方法,因而继承了 NSObject类的行为。然而某些情况下,NSObject
类仅仅定义了完成某件事情的模板,而没有提供所有需要的代码,某些 NSObject
的方法只是简单的从运行时系统中获得信息,从而允许对象进行一定程度的自我检查。如:class
返回对象的类:isKindOfClass:和
isMemberOfClass:则检查对象是否在指定的类继承体系中;respondsToSelector:检查对象能够相应指定的消息;conformsToProtocol:检查对象是否实现了指定协议类的方法;methodForSelector:则返回指定方法实现的地址。

    3、直接通过调用运行时系统的函数:

       
运行时系统是一个公开接口的动态库,由一些数据结构和函数的集合组成,这些数据结构和函数的声明头文件在/usr/include/objc
中。这些函数支持用纯 C 的函数来实现和 OC 同样的功能。浪游一些函数构成了
NSObject
类方法的基础。这些函数使得访问运行时系统接口和提供开发工具成为可能。尽管大部分情况下他们在
OC 程序中不是必须的,但是有时候对于 OC
这样的程序来说某些函数是非常有用的。

 

案例2:拦截系统方法

需求:比如手机尺寸需要版本适配,根据不同系统使用不同样式图片(拟物化和扁平化),如何通过不去手动一个个修改每个UIImage的imageNamed:方法就可以实现为该方法中加入版本判断语句?

步骤:1、为UIImage建一个分类(UIImage+Category)。
2、在分类中实现一个自定义方法,方法中写要在系统方法中加入的语句,比如版本判断。
3、分类中重写UIImage的load方法,实现方法的交换(只要能让其执行一次方法交换语句,load再合适不过了)。

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

#define iPhone4 (CGSizeEqualToSize(CGSizeMake(320, 480), [UIScreen mainScreen].bounds.size))
#define iPhone5 (CGSizeEqualToSize(CGSizeMake(320, 568), [UIScreen mainScreen].bounds.size))
#define iPhone6 (CGSizeEqualToSize(CGSizeMake(375, 667), [UIScreen mainScreen].bounds.size))
#define iPhone6plus (CGSizeEqualToSize(CGSizeMake(414, 736), [UIScreen mainScreen].bounds.size))

@implementation UIImage (Category)
+ (void)load {
    // 获取两个类的类方法
    Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:));
    Method m2 = class_getClassMethod([UIImage class], @selector(zdy_imageNamed:));
    // 开始交换方法实现
    method_exchangeImplementations(m1, m2);
}

+ (UIImage *)zdy_imageNamed:(NSString *)name {

    if (iPhone6plus) {
        // 如果苹果尺寸是6p,使用另外一套文件名结尾是‘_os7’的扁平化图片
        name = [name stringByAppendingString:@"_10"];
    }

    return [UIImage zdy_imageNamed:name];
}

在控制器中调用,会发现在6p上和6上,显示的图片不一样。

注意:自定义方法中最后一定要再调用一下系统的方法,让其有加载图片的功能,但是由于方法交换,系统的方法名已经变成了我们自定义的方法名(有点绕,就是用我们的名字能调用系统的方法,用系统的名字能调用我们的方法),这就实现了系统方法的拦截!
利用以上思路,我们还可以给 NSObject
添加分类,统计创建了多少个对象,给控制器添加分类,统计有创建了多少个控制器,特别是公司需求总变的时候,在一些原有控件或模块上添加一个功能,建议使用该方法!

3:具体代码
a 功能1 获取到类的所有实例变量(公有+私有)等等

 运行时的消息机制:

    1、获得方法地址:

       
避免动态绑定的唯一方法就是取得方法的地址,并且直接像函数调用一样调用它。当一个方法会被联系调用很多次,而且您希望节省每次调用方法都要发送消息的开销时,使用方法地址来调用方法就显得很有效。

        利用 NSObject 类中的
methodForSelector:方法,可以获得一个指向方法实现的指针,并可以使用该指针直接调用方法实现。methodForSelector:返回的指针和赋值的变量类型必须完全一致,包括方法的参数类型和返回值类型都在类型识别的考虑范围中。

       
在指定的消息被重复发送很多次时,避免动态绑定将减少大部分消息的开销。

 

    2、objc_msgSend 函数

        在 OC
中,消息是直到运行的时候才和方法实现绑定的,编译器会把一个消息表达式转换成一个对消息函数objc_msgSend
的调用。该函数有两个主要参数:消息接收者和消息对应的方法名字(也就是方法的选标)。

        objc_msgSend(receiver,
selector),同时接收消息中的任意数目的参数:objc_msgSend(receiver,
selector, arg1, arg2,…)

       
该消息函数做了动态绑定所需要的一切;找到对应的方法实现–>将消息接收者对象和参数传递给找到的方法实现–>将方法实现的返回值作为该函数的返回值返回 

        

        当对象收到消息时,消息函数首先根据该对象的 isa
指针找到该对象所对应的类的方法表,并从表中寻找该消息对应的方法选标,如果找不到,objc_msSend
将从父类中找,知道 NSObject 类。一旦找到了选标,objc_msgSend
则以消息接收者对象为参数调用,调用该选标对应的方法实现。这就是在运行时系统中选择方法实现的方式。在面向对象编程中一般称作方法和消息动态绑定的过程。

       
为了加快消息的处理过程,运行时系统通常会将使用过的方法选标和方法实现的地址放入缓存中。每个类都有一个独立的缓存,同时包括继承的方法和在该类中定义的方法。消息函数会首先检查消息接收者对象对应的类的缓存(理论上,如果一个方法被使用过一次,那么它很可能被再次使用)。如果在缓存中已经有了需要的方法选标,则消息仅仅比函数调用慢一点点。如果程序运行了足够长的时间,几乎每个消息都能在缓存中找到方法实现。程序运行时,缓存也将随着新的消息的增加而增加。

 

    3、使用隐藏的参数

        当 objc_msgSend
找到方法对应的实现时,它将直接调用该方法的实现,并将消息中所有的参数都传递给方法实现,同时,还将传递两个隐藏的参数:

            1、接受消息的对象:可以通过 self 来引用消息接收者对象

            2、方法选标:通过选标_cmd 来引用方法本身。

        尽管这些参数没有被显示声明,但在源代码中仍然可以引用它们。

 

二、在分类中设置属性,给任何一个对象设置属性

分类中是无法设置属性的,如果在分类的声明中写@property 只能为其生成get
和 set
方法的声明,但无法生成成员变量,就是虽然点语法能调用出来,但程序执行后会crash,有人会想到使用全局变量呢?

需要用到的方法 <objc/runtime.h>
1.set方法,将值value 跟对象object 关联起来(将值value 存储到对象object
中)参数 object:给哪个对象设置属性
参数 key:一个属性对应一个Key,将来可以通过key取出这个存储的值,key
可以是任何类型:double、int 等,建议用char 可以节省字节
参数 value:给属性设置的值
参数policy:存储策略 (assign 、copy 、 retain就是strong)

void objc_setAssociatedObject(id object , const void *key ,id value ,objc_AssociationPolicy policy)

2.利用参数key 将对象object中存储的对应值取出来

id objc_getAssociatedObject(id object , const void *key)

步骤:
1、创建一个分类,比如给任何一个对象都添加一个name属性,就是NSObject添加分类(NSObject+Category)
2、先在.h 中@property 声明出get 和 set 方法,方便点语法调用

@property(nonatomic,copy)NSString *name;

3、在.m 中重写set 和 get 方法,内部利用runtime 给属性赋值和取值

char nameKey;
- (void)setName:(NSString *)name { 
// 将某个值跟某个对象关联起来,将某个值存储到某个对象中 
objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name { 
return objc_getAssociatedObject(self, &nameKey);
}
#import <Foundation/Foundation.h>

@interface Person : NSObject<NSCoding>

@property (nonatomic,strong)NSString *name;
@property (nonatomic,strong)NSString *sex;
@property (nonatomic,strong)NSString *number;

- (void)sayHi;

@end

#import "Person+hello.h"
#import "Person.h"
#import <objc/message.h>
#import <objc/runtime.h>

@interface Person ()

@property (nonatomic,strong)NSString *address;

@end

@implementation Person

- (instancetype)init{
    if (self = [super init]) {
        self.name = @"明明";
        self.sex = @"女";
        self.number = @"123";
        self.address = @"中南海";
    }
    return self;
}

- (void)sayHi{
    NSLog(@"sayHi");
}

#import "Person.h"
#import "Person+hello.h"
#import <objc/message.h>
#import <Foundation/Foundation.h>


int main(int argc, const char * argv[]) {
    @autoreleasepool {

        //runtime功能1 获取到类的所有实例变量(公有+私有)等等
        //创建person对象
        Person *p = [[Person alloc]init];
        unsigned int count = 0;
        //获取到指向类的实例变量的指针
        Ivar *ivars = class_copyIvarList([p class], &count);
        for (int i = 0; i < count; i++) {
            Ivar var = ivars[i];
            const char *c = ivar_getName(var);
//            NSLog(@"%s",c);
        }

    }
    return 0;
}

 动态方法解析:

    1、动态方法解析:

        @dynamic property name; 表示编译器需动态的生成该属性对应的方法。

        可以通过实现 resolveInstanceMethod:和
resolveClassMethod:来动态的实现给定选标的对象方法或者类方法。

        OC 方法可以认为是至少有两个参数 self 和_cmd 的 C 函数。可以通过
class_addMethod 方法将一个函数加入到类的方法中,如下:

         

         void dynamicMethodIMP(id self, SEL _cmd) {

            // implementation…

         }

         + (BOOL)resolveInstanceMethod:(SEL)sel {

             if (sel == @selector(resolveThisMethodDynamically)) {

             class_addMethod([self class], sel, (IMP)
dynamicMethodIMP,
“[email protected]:”);

             return YES;

         }

             return [super resolveInstanceMethod:sel];

         }

 

        在进入消息转发机制之前,respondsToSelector:和
instancesRespondToSelector:会被首先调用。可以在这两个方法中为传进来的选标提供一个
IMP。如果您实现了
resolveInstanceMethod:方法但是仍然希望正常的消息转发机制进行,只需要返回
NO 就可以了。

 

    2、动态加载

        OC
程序可以在运行时链接和载入新的类和范畴类。新载入的类和在程序启动时载入的类并没有区别。

       
动态加载可以用在很多地方,例如,系统配置中的模块就是被动态加载的。

 

        应用场景:

            在 Cocoa
环境中,动态加载一般被用来对应用程序进行定制。可以在运行时加载其他程序员编写的模块(和
interface
Build载入定制的调色板以及系统配置程序载入定制的模块类似)。这些模块通过许可的方式扩展了自身的程序,而无需自己来定义或者实现。自己提供了框架,二其他程序员提供了实现。

 

三、获得一个类的所有成员变量

案例1:获取Person类中所有成员变量的名字和类型

需要用到的方法 <objc/runtime.h>

  • 获得某个类的所有成员变量(outCount 会返回成员变量的总数)参数:
    1、哪个类
    2、放一个接收值的地址,用来存放属性的个数
    3、返回值:存放所有获取到的属性,通过下面两个方法可以调出名字和类型

Ivar *class_copyIvarList(Class cls , unsigned int *outCount)
  • 获得成员变量的名字
    const char *ivar_getName(Ivar v)
  • 获得成员变量的类型

const char *ivar_getTypeEndcoding(Ivar v)

b : 获取到类的所有方法

 消息转发:

     1、消息转发

       
如果一个对象收到一条无法处理的消息,运行时系统会在抛出错误前,给该对象发送一条
forwardInvocation:消息,该消息的唯一参数是个 NSInvocation
类型的对象,该对象封装了原始的消息和消息的参数。所以可以实现
forwardInvocation:
方法来对不能处理的消息做一些默认的处理,也可以以其它的某种方式来避免错误被抛出。

        当一个对象没有响应的方法实现而无法响应某消息时,运行时系统将通过
forwardInvocation: 消息通知该对象。每个对象都从 NSObject 类中集成了
forwardInvocation: 方法。然而,NSObject 中的方法实现只是简单的调用了
doerNotRecognizeSelector:。 通过实现自己的 forwardInvocation:
方法可以在该方法实现中将消息转发给其他对象。

        消息可以通过 invokeWithTarget:方法来转发:

        

         – (void)forwardInvocation:(NSInvocation *)anInvocation {

             if ([someOtherObject respondsToSelector:[anInvocation
selector]])

                 [anInvocation invokeWithTarget:someOtherObject];

             else

                 [super forwardInvocation:anInvocation];

         }

     

       
forwardInvocation:方法就像一个不能识别的消息的分发中心,将这些消息转发给不同接收对象。它可以将一个消息翻译成另外一个消息,或者简单的“吃掉”某些消息,因此没有响应也没有错误。forwardInvocation:方法也可以对不同的消息提供同样的相应,这一切都取决于方法的具体实现。该方法所提供是将不通的对象链接到消息链的能力。

     

       
注意:forwardInvocation:方法只有在消息接受对象中无法正常响应消息时才会被调用。所以,如果您希望您的对象将
clickBtn:消息转发给其他对象,您的对象不能有
clickBtn:方法。否则,forwardInvocation:将不可能会被调用。

     

     2、消息转发和多重继承

        消息转发很像集成,并且可以用来在 OC
程序中模拟多重继承。一个对象通过转发来响应消息,看起来就像该对象从别的类那里借来了或者“继承”了方法实现一样。通过
forwardInvocation: 方法将一个类中的消息转发给另一个类.

     3、消息代理对象

     4、消息转发和类继承

本篇主要讲述在 OC 开发中主要涉及到的运行时机制: 运行时的工作: 运行时在
OC 中的工作:…

案例1:获取Person类中所有成员变量的名字和类型

Person.h

@property (nonatomic,copy) NSString *name;

Person.m

- (NSString *)names {
    return _name;
}

- (void)setName:(NSString *)names {
    _name = names;
}

控制器:

 unsigned int outCount = 0;
    Ivar *ivars = class_copyIvarList([Person class], &outCount);

    // 遍历所有成员变量
    for (int i = 0; i < outCount; i++) {
        // 取出i位置对应的成员变量
        Ivar ivar = ivars[i];
        const char *name = ivar_getName(ivar);
        const char *type = ivar_getTypeEncoding(ivar);
        NSLog(@"成员变量类型:%s 成员变量名:%s ",type,name);
    }
    // 注意释放内存!
    free(ivars);

打印的结果为:

2016-11-04 11:45:11.352 RunTime[2305:175512] 成员变量类型:
@”NSString” 成员变量名: _name

别的方法还在探索中,持续更新。。。

        //main.m中 获取到类的所有方法
        Method *methods = class_copyMethodList([p class], &count);
        for (int i = 0; i < count; i++) {
            Method method = methods[i];
            SEL s = method_getName(method);
            const char *name = sel_getName(s);
//            NSLog(@"%s",name);
        }

c :
为分类添加属性(个人觉得意义并不大,就使用oc也能做到,只是它不会关联到self)

#import "Person.h"

@interface Person (hello)

//分类添加的属性不会生成对应的实例变量
@property (nonatomic,strong)NSString *age;

@end

#import <objc/runtime.h>
#import "Person+hello.h"

@implementation Person (hello)

static const void *key = @"123";

- (void)setAge:(NSString *)age{
    //对象关联
     //self为要关联的对象
     //key为要关联的变量
     //age为要被关联的变量
    //第四个参数为key修饰符
    objc_setAssociatedObject(self, key, age, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSString *)age{
    //返回关联的对象
    return objc_getAssociatedObject(self, key);
}

//main.m中使用此属性
p.age = @"123";
NSLog(@"%@",p.age);

d : runtime功能4
实现方法交换(当我们对系统方法的实现不满意的时候,我们可以通过方法交换来自定义实现)

#import "Person.h"

@interface Person (hello)

- (void)play;

@end

#import <objc/runtime.h>
#import "Person+hello.h"

@implementation Person (hello)

+ (void)load{
    //获取到方法
    Method sayHi = class_getInstanceMethod([self class], @selector(sayHi));
    Method play = class_getInstanceMethod([self class], @selector(play));

    //实现方法交换
    method_exchangeImplementations(sayHi, play);

}

//实现方法play和sayHi方法的交换
- (void)play{
    NSLog(@"play");
}

//在main.m中调用可以看到打印出来 play
 [p sayHi];

e: 实现字典和模型之间的互相转换

 Person *p = [[Person alloc]init];
        //定义一个字典用来赋值给model
        NSDictionary *dic = @{
                              @"name" : @"隔壁老王",
                              @"sex":@"男",
                              @"number":@"1111",
                              @"address":@"😄"
                              };

        //定义一个空字典用来存储model
        NSMutableDictionary *dic1 = [NSMutableDictionary dictionary];
         //用来存储实例变量的个数
        unsigned int count = 0;
        //获取到指向类的实例变量的指针
        Ivar *ivars = class_copyIvarList([p class], &count);
        //遍历
        for (int i = 0; i < count; i++) {
            Ivar var = ivars[i];
            const char *c = ivar_getName(var);
            //获取到字符串
            NSString *s = [NSString stringWithUTF8String:c];
            //去除下划线(_)
            NSString *s1 = [s substringFromIndex:1];
            //a 将模型和字典对应
            id value = [p valueForKey:s1];
            [dic1 setValue:value forKey:s1];

            //b 将字典和模型对应
            [p setValue:dic[s1] forKey:s1];
        }
        NSLog(@"dic1 = %@",dic1);

相关文章