iOS runtime运行时的用法(一)

原理解说:https://www.jianshu.com/p/19f280afcb24

https://blog.csdn.net/jq2530469200/article/details/51886578

<span style="color:#333333">RunTime简称运行时。OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制。</span>
 

编译时: 即编译器对语言的编译阶段,编译时只是对语言进行最基本的检查报错,包括词法分析、语法分析等等,将程序代码翻译成计算机能够识别的语言(例如汇编等),编译通过并不意味着程序就可以成功运行。

运行时: 即程序通过了编译这一关之后编译好的代码被装载到内存中跑起来的阶段,这个时候会具体对类型进行检查,而不仅仅是对代码的简单扫描分析,此时若出错程序会崩溃。

可以说编译时是一个静态的阶段,类型错误很明显可以直接检查出来,可读性也好;而运行时则是动态的阶段,开始具体与运行环境结合起来。

 

OC语言的动态性主要体现在三个方面:动态类型(Dynamic typing)、动态绑定(Dynamic binding)和动态加载(Dynamic loading)。

动态类型指的是对象指针类型的动态性,具体是指使用id任意类型将对象的类型确定推迟到运行时,由赋给它的对象类型决定对象指针的类型。另外类型确定推迟到运行时之后,可以通过NSObject的isKindOfClass方法动态判断对象最后的类型(动态类型识别)。也就是说id修饰的对象为动态类型对象,其他在编译器指明类型的为静态类型对象,通常如果不需要涉及到多态的话还是要尽量使用静态类型(原因上面已经说到:错误可以在编译器提前查出,可读性好)。(编译的时候不会检查对象的类型,运行的时候才会检查对象的类型,)

动态绑定指的是方法确定的动态性,具体指的是利用OC的消息传递机制将要执行的方法的确定推迟到运行时,可以动态添加方法。也就是说,一个OC对象是否调用某个方法不是由编译器决定的,而是由运行时决定的;另外关于动态绑定的关键一点是基于消息传递机制的消息转发机制,主要处理应对一些接受者无法处理的消息,此时有机会将消息转发给其他接收者处理,具体见下面介绍。(编译的时候不会决定执行那个方法,运行的时候给对象发送消息,决定执行那个方法)

动态绑定是基于动态类型的,在运行时对象的类型确定后,那么对象的属性和方法也就确定了(包括类中原来的属性和方法以及运行时动态新加入的属性和方法),这也就是所谓的动态绑定了。动态绑定的核心就该是在运行时动态的为类添加属性和方法,以及方法的最后处理或转发,主要用到C语言语法,因为涉及到运行时,因此要引入运行时头文件:#include <objc/runtime.h>

消息传递机制:

在OC中方法的调用实际不是对象调用其方法,而是要理解成对象接受消息,方法的调用实际就是告诉对象要干什么,给对象(的指针)传送一个消息。

 

 

对于C语言,函数的调用在编译的时候会决定调用哪个函数。

对于OC的函数,属于动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。

事实证明:

·在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错。

·在编译阶段,C语言调用未实现的函数就会报错。

///OC方法调用的本质,就是给对象发送消息。

在 Objective-C 中,执行 [object  foo] 语句并不会立即执行 foo 这个方法的代码。它是在运行时给 object 发送一条叫 foo 的消息。这个消息,也许会由 object 来处理,也许会被转发给另一个对象,或者不予理睬假装没收到这个消息。多条不同的消息也可以对应同一个方法实现。这些都是在程序运行的时候决定的。

编译时你写的 Objective-C 函数调用的语法都会被翻译成一个 C 的函数调用 - objc_msgSend()。比如,下面两行代码就是等价的:

 

 

    [p eat];    // 本质:给对象发送消息
    objc_msgSend(p, @selector(eat));// 本质:给对象发送消息
    objc_msgSend(p, @selector(eat));

消息传递过程: 

 

  1. 首先,找到 object 的 class; 
  2. 通过 class 找到 foo 对应的方法实现; 
  3. 如果 class 中没到 foo,继续往它的 superclass 中找,一直找到NSObjct类为止; 
  4. 一旦找到 foo 这个函数,就去执行它的实现
  5. 如果没找到就进入运行时的动态方法解析,可以用运行时添加方法;

消息直到运行时才会与方法实现进行绑定。

这里要清楚一点,objc_msgSend 方法看清来好像返回了数据,其实objc_msgSend 从不返回数据,而是你的方法在运行时实现被调用后才会返回数据。

 

  1. 首先检测这个 selector 是不是要忽略。比如 Mac OS X 开发,有了垃圾回收就不理会 retain,release 这些函数。
  2. 检测这个 selector 的 target 是不是 nil,Objc 允许我们对一个 nil 对象执行任何方法不会 Crash,因为运行时会被忽略掉。
  3. 如果上面两步都通过了,那么就开始查找这个类的实现 IMP,先从 cache 里查找,如果找到了就运行对应的函数去执行相应的代码。
  4. 如果 cache 找不到就找类的方法列表中是否有对应的方法。
  5. 如果类的方法列表中找不到就到父类的方法列表中查找,一直找到 NSObject 类为止。
  6. 如果还找不到,就要开始进入动态方法解析了。

在消息的传递中,编译器会根据情况在 objc_msgSend , objc_msgSend_stret , objc_msgSendSuper , objc_msgSendSuper_stret 这四个方法中选择一个调用。如果消息是传递给父类,那么会调用名字带有 Super 的函数,如果消息返回值是数据结构而不是简单值时,会调用名字带有 stret 的函数。

 

 

 

动态方法解析会在消息转发机制侵入前执行,动态方法解析器将会首先给予提供该方法选择器对应的 IMP 的机会。如果你想让该方法选择器被传送到转发机制,就让 resolveInstanceMethod: 方法返回 NO

 

消息转发

 

 

重定向

 

消息转发机制执行前,Runtime 系统允许我们替换消息的接收者为其他对象。通过 - (id)forwardingTargetForSelector:(SEL)aSelector 方法。

- (id)forwardingTargetForSelector:(SEL)aSelector
{
    if(aSelector == @selector(mysteriousMethod:)){
        return alternateObject;
    }
    return [super forwardingTargetForSelector:aSelector];
}

如果此方法返回 nil 或者 self,则会计入消息转发机制(forwardInvocation:),否则将向返回的对象重新发送消息。

转发

当动态方法解析不做处理返回 NO 时,则会触发消息转发机制。这时 forwardInvocation: 方法会被执行,我们可以重写这个方法来自定义我们的转发逻辑:

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    if ([someOtherObject respondsToSelector:
            [anInvocation selector]])
        [anInvocation invokeWithTarget:someOtherObject];
    else
        [super forwardInvocation:anInvocation];
}

唯一参数是个 NSInvocation 类型的对象,该对象封装了原始的消息和消息的参数。我们可以实现 forwardInvocation: 方法来对不能处理的消息做一些处理。也可以将消息转发给其他对象处理,而不抛出错误。

注意:参数 anInvocation 是从哪来的?
在 forwardInvocation: 消息发送前,Runtime 系统会向对象发送methodSignatureForSelector: 消息,并取到返回的方法签名用于生成 NSInvocation 对象。所以重写 forwardInvocation: 的同时也要重写 methodSignatureForSelector: 方法,否则会抛异常。

当一个对象由于没有相应的方法实现而无法相应某消息时,运行时系统将通过 forwardInvocation: 消息通知该对象。每个对象都继承了 forwardInvocation: 方法。但是, NSObject 中的方法实现只是简单的调用了 doesNotRecognizeSelector:。通过实现自己的 forwardInvocation: 方法,我们可以将消息转发给其他对象。

forwardInvocation: 方法就是一个不能识别消息的分发中心,将这些不能识别的消息转发给不同的接收对象,或者转发给同一个对象,再或者将消息翻译成另外的消息,亦或者简单的“吃掉”某些消息,因此没有响应也不会报错。这一切都取决于方法的具体实现。

注意:
forwardInvocation:方法只有在消息接收对象中无法正常响应消息时才会被调用。所以,如果我们向往一个对象将一个消息转发给其他对象时,要确保这个对象不能有该消息的所对应的方法。否则,forwardInvocation:将不可能被调用。

 


==========================

使用消息机制前提,必须导入#import <objc/message.h>

消息机制简单使用

 
    Person *p = [[Person alloc] init];    // 调用对象方法
    [p eat];    // 本质:给对象发送消息
    objc_msgSend(p, @selector(eat));   
 // 调用类方法的方式:两种
    // 第一种通过类名调用
    [Person eat];    // 第二种通过类对象调用
    [[Person class] eat];

    // 用类名调用类方法,底层会自动把类名转换成类对象调用
    // 本质:让类对象发送消息
    objc_msgSend([Person class], @selector(eat));// 调用类方法的方式:两种
    // 第一种通过类名调用
    [Person eat];    // 第二种通过类对象调用
    [[Person class] eat];

    // 用类名调用类方法,底层会自动把类名转换成类对象调用
    // 本质:让类对象发送消息
    objc_msgSend([Person class], @selector(eat));

 

运行时的应用:

 

1.将某些OC代码转为运行时代码,探究底层,比如block的实现原理(上边已讲到);
2.拦截系统自带的方法调用(Swizzle 黑魔法),比如拦截imageNamed:、viewDidLoad、alloc;
3.实现分类也可以增加属性;
4.实现NSCoding的自动归档和自动解档;
5.实现字典和模型的自动转换。

 

 

 

=================动态添加成员变量======================================

/**
 runtime动态添加属性
 */

#import <Foundation/Foundation.h>
#import <objc/message.h>
NS_ASSUME_NONNULL_BEGIN

@interface NSObject (Property)
// @property分类: 只会生成get,set方法声明,不会生成方法实现,并且不会生成_name私有属性
@property(nonatomic,copy) NSString *userName;
@end

NS_ASSUME_NONNULL_END


/**
 runtime动态添加属性
 */

#import "NSObject+Property.h"
// 属性名称
static const char * key = "name";//必须用char *,用NSString会报错
@implementation NSObject (Property)
// set方法
- (void)setUserName:(NSString *)userName
{
    // 让这个字符串与当前对象产生联系
    /*
     param1:object:给哪个对象添加属性
     param2:key:属性名称
     param3:value:属性值
     param4:policy:保存策略
     */
    objc_setAssociatedObject(self,key, userName, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

// get 方法
- (NSString *)userName
{
    return objc_getAssociatedObject(self, key);
}

@end

/*

 1. 分类不可以扩充成员变量

 2. 分类可以扩充属性,但是属性不可以保存值.因为分类中没有成员变量.我们需要重写setter和getter

 3. 分类可以扩充方法

 */

 

//设置值

- (void)setLastUrl:(NSString *)lastUrl

{

    /*

     1. 需要关联的对象 self

     2. 需要关联的对象的属性的键值(key)

     3. 需要关联的对象的属性lasturl

     4. 需要关联的对象的属性的修饰符(策略)

     */

    objc_setAssociatedObject(self,"key", lastUrl,OBJC_ASSOCIATION_COPY_NONATOMIC);

}

 

//获取值

- (NSString *)lastUrl

{

    /*

     1. 需要关联的对象 self

     2. 需要关联的对象的属性的键值(key)

     */

    returnobjc_getAssociatedObject(self,"key");

}

 

=========runtime 实现方法交换:=================

/**
 runtime实现交换方法
 */

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface UIImage (RuntimeExchangeMethod)
+ (UIImage *)xmg_imageNamed:(NSString *)name;
@end

NS_ASSUME_NONNULL_END


//

#import "UIImage+RuntimeExchangeMethod.h"
#import <objc/message.h>
@implementation UIImage (RuntimeExchangeMethod)
// 把类加载进内存的时候调用,只会调用一次
+ (void)load
{
    // self -> UIImage
    // 获取imageNamed
    // 获取哪个类的方法
    // SEL:获取哪个方法
    Method imageNamedMethod = class_getClassMethod(self, @selector(imageNamed:));
    // 获取xmg_imageNamed
    Method xmg_imageNamedMethod = class_getClassMethod(self, @selector(xmg_imageNamed:));
    
    // 交互方法:runtime
    method_exchangeImplementations(imageNamedMethod, xmg_imageNamedMethod);
    // 调用imageNamed => xmg_imageNamedMethod
    // 调用xmg_imageNamedMethod => imageNamed
}



// 在分类中,不要重写系统方法,一旦重写,把系统方法实现给干掉

//+ (UIImage *)imageNamed:(NSString *)name
//{
//    // super -> 父类NSObject
//
//}

// 1.加载图片
// 2.判断是否加载成功
+ (UIImage *)xmg_imageNamed:(NSString *)name
{
    // 图片
    UIImage *image = [UIImage xmg_imageNamed:name];

    if (image) {
        NSLog(@"加载成功");
    } else {
        NSLog(@"加载失败");
    }
    
    return image;
}
@end

 

#import <UIKit/UIKit.h>

 

@interface UIImage (XL)

+ (instancetype)imageWithName:(NSString *)name;

@end

///////////////

 

 

#import "UIImage+XL.h"

#import <objc/runtime.h>

@implementation UIImage (XL)

 

+ (void)load

{

    Method m1 =class_getClassMethod([UIImageclass],@selector(imageWithName:));

    Method m2 =class_getClassMethod([UIImageclass],@selector(imageNamed:));;

    method_exchangeImplementations(m1, m2);

}

 

+ (instancetype)imageWithName:(NSString *)name

{

       

    UIImage *image = [UIImage imageWithName:imageName];

    if (image ==nil) {

        image = [UIImageimageWithName:name];

    }

    

    

    return image;

    

}

@end

===================================

 

#import <Foundation/Foundation.h>

 

@interface NSMutableArray (XL)

 

@end

///////////

 

#import "NSMutableArray+XL.h"

#import <objc/runtime.h>

@implementation NSMutableArray (XL)

 

 

+ (void)load

{

    Method m1 =class_getInstanceMethod(NSClassFromString(@"__NSArrayM"),@selector(cz_addObjct:));

    Method m2 =class_getInstanceMethod(NSClassFromString(@"__NSArrayM"),@selector(addObject:));;

    method_exchangeImplementations(m1, m2);

}

 

- (void)cz_addObjct:(id)objc

{

 

    if (objc ==nil) {

        [selfcz_addObjct:@"you are sb! is nil "];

        return;

       

    }

    [selfcz_addObjct:objc];

}

@end

===================================

交换原理:当一个类加载到内存中时需要给每个类方法或对象方法生成方法映射表,需要调用方法的时候根据方法编号(SEL类型的方法选择器)到方法映射表中去寻查询,然后找到对应的函数实现;

·交换之前:

·交换之后:

 

==========动态的添加方法================


/**
 runtimed动态添加方法,
 使用:
 AddDynamicMethodVC *addDynamicMethodVC=[[AddDynamicMethodVC alloc]init];
 [addDynamicMethodVC performSelector:@selector(eat)];//调用动态添加的方法时要用perfermSelector
 */

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface AddDynamicMethodVC : UIViewController

@end

NS_ASSUME_NONNULL_END



/**
 下面是动态添加方法的实现
 */

#import "AddDynamicMethodVC.h"
#import <objc/message.h>
@interface AddDynamicMethodVC ()

@end

@implementation AddDynamicMethodVC

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}
// 这个是新添加的方法,默认方法都有两个隐式参数,
void eat(id self,SEL sel)
{
    NSLog(@"%@--------- %@",self,NSStringFromSelector(sel));
}

// 当一个对象调用未实现的方法,会调用这个方法处理,并且会把对应的方法列表传过来.
// 刚好可以用来判断,未实现的方法是不是我们想要动态添加的方法
+ (BOOL)resolveInstanceMethod:(SEL)sel
{

    if (sel== NSSelectorFromString(@"eat")) {
        // 动态添加eat方法

        // 第一个参数:给哪个类添加方法
        // 第二个参数:添加方法的方法编号
        // 第三个参数:添加方法的函数实现(函数地址)
        // 第四个参数:函数的类型,(返回值+参数类型) v指返回值void @指对象->self等参数 :表示SEL->_cmd
        class_addMethod(self, NSSelectorFromString(@"eat"), (IMP)eat, "v@:");

    }

    return [super resolveInstanceMethod:sel];
}



@end
一个c语言函数方法都有两个隐式参数:

 

void dynamicMethodIMP(id selfSEL _cmd)

{

    NSLog(@"//动态添加的方法在这里实现");

}

 

 

 

 

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

       如果一个类方法非常多,加载类到内存的时候也比较耗费资源,需要给每个方法生成映射表,可以使用动态给某个类,添加方法解决;

 

        在OC中,所有的控件(textFiled或者button等控件), 数组, 数据等都是以懒加载的形式加载的.真正使用的时候才会加载,或者添加方法.动态添加的方法的作用就是去处理未实现的实例方法或者是类方法.它的调用时刻: 只要我们调用了一个不存在的方法时,它就会动态添加方法.

        动态添加方法之前我们还需要判断当前的方法有没有实现,如果没有实现才需要动态添加方法.

         作为一种动态编程语言,Objective-C 拥有一个运行时系统来支持动态创建类,添加方法、进行消息传递和转发。

           Objective-C 运行时会调用 +resolveInstanceMethod: 或者 +resolveClassMethod:,让你有机会提供一个函数实现。如果你添加了函数并返回 YES, 那运行时系统就会重新启动一次消息发送的过程。

           如果 resolveInstanceMethod: 方法返回 NO,运行时就会进行下一步:消息转发(Message Forwarding)。

 

动态去判断下eat方法有没有实现,如果没有实现,动态添加.
// 作用:处理未实现的对象方法
// 调用时刻:只要调用了一个不存在的对象方法就会调用
// sel:就是未实现方法编号

// 判断对象方法有没有实现
+(BOOL)resolveInstanceMethod:(SEL)sel

// 判断类方法有没有实现
+ (BOOL)resolveClassMethod:(SEL)sel

 

Class cls

 

cls 参数表示需要添加新方法的类。

1

SEL name

name 参数表示 selector 的方法名称,可以根据喜好自己进行命名。

1

IMP imp

imp 即 implementation ,表示由编译器生成的、指向实现方法的指针。也就是说,这个指针指向的方法就是我们要添加的方法。

1

const char *types

最后一个参数 *types 表示我们要添加的方法的返回值和参数。


/ 参数解释:
        // Class;给哪个类添加方法
        // SEL:要添加的方法
        // IMP:方法实现,函数名(实现却是在另外一个方法,和添加的方法不一样)
        // types:函数的类型,(返回值+参数类型) v:void @:对象->self :表示SEL->_cmd
class_addMethod(__unsafe_unretained Class cls, SEL name, IMP imp, const char *types)

 

 

#import <UIKit/UIKit.h>

 

@interface ViewController : UIViewController

-(void)resolveThisMethodDynamically;//动态添加的是这个方法,但是实现却是在另外一个方法中

@end

 

==================

在.m中

 

- (void)viewDidLoad {

    [superviewDidLoad];

 

    [selfresolveThisMethodDynamically];//调用的是没有实现的方法,是动态添加的

    

 

}

 

//动态添加的方法在这里实现 

void dynamicMethodIMP(idself, SEL_cmd)

{

    NSLog(@"//动态添加的方法在这里实现");

}

 

 

+ (BOOL) resolveInstanceMethod:(SEL)aSEL

{

    if (aSEL ==@selector(resolveThisMethodDynamically))

    {

        class_addMethod([selfclass], aSEL, (IMP)dynamicMethodIMP, "v@:");

        returnYES;

    }

    return [superresolveInstanceMethod:aSEL];

}

 

 

 

===========获取去对象的属性和方法=============


/**
 runtime获取成员变量,以及方法等
 */

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSObject (RuntimeGetProperty)

@end

NS_ASSUME_NONNULL_END


#import "NSObject+RuntimeGetProperty.h"
#import <objc/message.h>
@implementation NSObject (RuntimeGetProperty)
-(void)getProperty{
    unsigned int count;
    Method *methods = class_copyMethodList([UITextView class], &count);//获取所有方法
    for (int i = 0; i < count; i++) {
        Method method = methods[i];
        SEL selector = method_getName(method);//获取方法
        NSString *name = NSStringFromSelector(selector);
        
        NSLog(@"method_getName:%@",name);
    }
    
    unsigned int numIvars;
    Ivar *vars = class_copyIvarList([UITextView class], &numIvars);//获取所有的属性
    NSString *key=nil;
    for(int i = 0; i < numIvars; i++) {
        
        Ivar thisIvar = vars[i];
        const char *memberName = ivar_getName(thisIvar);//获取属性名
        key = [NSString stringWithUTF8String:memberName];
        NSLog(@"variable_name :%@", key);
        
        const char * type = ivar_getTypeEncoding(thisIvar);//获取变量类型
        NSString *typeStr = [NSString stringWithCString:type encoding:NSUTF8StringEncoding];
        if ([typeStr isEqualToString:@"@\"NSString\""]){
            //修改属性的值
            object_setIvar(self, thisIvar, @"abc");
        }
        
        //获取指定名称的成员变量
        Ivar ivar = class_getInstanceVariable([self class], memberName);
        
    }
    free(vars);
}
@end

runtime的方法:

 

unsigned int propertyCount = 0;
    objc_property_t *properties = class_copyPropertyList(klass, 
&propertyCount);//获取所有的属性对象


    for (unsigned int i = 0; i < propertyCount; ++i) {
        objc_property_t property = properties[i];
        const char * name = property_getName(property);//获取属性名字
        const char * attributes = property_getAttributes(property);//获取属性类型
}
    objc_property_t *properties = class_copyPropertyList(klass, 
&propertyCount);//获取所有的属性对象


    for (unsigned int i = 0; i < propertyCount; ++i) {
        objc_property_t property = properties[i];
        const char * name = property_getName(property);//获取属性名字
        const char * attributes = property_getAttributes(property);//获取属性类型
}

 

 

 

 

  • 获得某个类的所有成员变量(outCount 会返回成员变量的总数)
    参数:
    1、哪个类
    2、放一个接收值的地址,用来存放属性的个数
    3、返回值:存放所有获取到的属性,通过下面两个方法可以调出名字和类型
    Ivar *class_copyIvarList(Class cls , unsigned int *outCount)
  • 获得成员变量的名字(带下划线 _name)
    const char *ivar_getName(Ivar v)
  • 获得成员变量的类型
    const char *ivar_getTypeEndcoding(Ivar v)

     

 

 

 

 

类型编码

 

为了帮助运行时系统,编译器将每个方法中的返回和参数类型进行编码,并将该字符串与该方法选择器关联。在其他情况下,编码体系也是很有用的,所以编码体系是带有@encode()编译指令的工公共的可用的。当给一个指定类型,@encode()返回指定的类型的字符串编码。这个类型可以是任何类型,可以是基本类型,如int型指针,可以是一个标记结构或联合,或类名,可以被C语言的sizeof()运算符作为参数使用。
下面的表格列出了编码类型。注意当对一个对象归档或者分发时,他们中的许多代码与你使用的代码重叠。然而,这些列表中的编码在你归档的时候不能使用他们,你可能想要在归档使用那些不是@encode()生成的代码。

编码类型

 

计算机唯一能识别的语言是机器语言,高级编程语言不能被直接识别,需要先编译为汇编语言,再由汇编语言编译
为机器语言才能被计算机识别。而 Objective-C语言不能被直接编译为汇编语言,它必须先编译为C语言,然后再
编译为汇编语言,最后再由汇编语言编译为机器语言才能被计算机识别。 从OC到C语言的过渡就是由runtime来实
现的。我们使用OC进行面向对象开发,但是C语言更多的是面向过程开发,这就需要将面向对象的类转变为面向过
程的结构体。

. Runtime 是iOS中的一个运行时系统,是苹果用C语言和汇编语言编写的一套底层纯C语言API。

2. Runtime正是 Objective-C这门动态语言的核心(数据类型的确定由编译时推迟到运行时)。从OC到C语言的过
渡就是由Runtime来实现的。

3. OC代码最终都会被编译器转化为运行时代码,通过消息机制决定函数调用方式(objc_msgSend)。

4. OC类、对象和方法等都会被Runtime转化成C语言中的结构体。

5. 在Runtime中,id 是一个指向 objc_object 结构体的指针;class 是一个指向 objc_class 结构体的指针

6. SEL 是一个指向 objc_selector 结构体的指针; Ivar 是一个指向 objc_ivar 的结构体的指针

7. Method 是一个指向 objc_method 的结构体的指针;IMP 是一个函数指针

使用:
1. 动态方法交换  (method_exchangeImplementations)
2. 给分类添加属性 (objc_setAssociatedObject 和 objc_getAssociatedObject)
3. 获取类的详细信息
   3.1 属性列表(class_copyPropertyList)
   3.2 获取成员变量(class_copyIvarList)
   3.3 获取所有方法(class_copyMethodList)
   3.4 获取当前遵循的所有协议(class_copyProtocolList)
4. 解决同一方法高频率调用的效率问题
5. 方法动态解析与消息转发
   5.1 动态添加方法
   5.2 解决方法无响应崩溃问题
6. 动态操作属性
  6.1 动态修改属性变量
  6.2 实现 NSCoding 的自动归档和解档
  6.3 实现字典与模型的转换

https://www.jianshu.com/p/51b20fcdf7bc

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值