ios-runtime添加方法

runtime官方
Objective-C Runtime Programming Guide - Dynamic Method Resolution

1、添加方法
  • 开发使用场景:如果一个类方法非常多,加载类到内存的时候也比较耗费资源,需要给每个方法生成映射表,可以使用动态给某个类,添加方法解决。
  • 经典面试题:有没有使用performSelector,其实主要想问你有没有动态添加过方法
    方法:
    OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp,
    const char *types)
+ (BOOL)resolveInstanceMethod:(SEL)se//动态对象方法
+ (BOOL)resolveClassMethod:(SEL)sel;//动态类方法
通过重写上面方法,调用class_addMethod();函数来动态添加方法,同时返回YES即可,如果返回NO,则会进入消息转发机制.

写法一:

//可以根据这个方法来取到

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self performSelector:@selector(drive)];
}

void startEngine(id self, SEL _cmd, NSString *brand) { 
    NSLog(@"my %@ car starts the engine", brand);
}

+ (BOOL)resolveInstanceMethod:(SEL)sel { 
    if (sel == @selector(drive)) { 
        class_addMethod([self class], sel, (IMP)startEngine, "v@:@"); 
        return YES; 
    } 
    return [super resolveInstanceMethod:sel];
}

写法二:使用oc的方法

//使用这个来调用

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self performSelector:@selector(drive)];
}

+ (BOOL)resolveInstanceMethod:(SEL)sel { 
    if (sel == @selector(drive)) { 
////获取类中的某个实例方法(减号方法)
Method exchangeM = class_getInstanceMethod([self class], @selector(startEngine:));
        class_addMethod([self class], sel, class_getMethodImplementation(self, @selector(startEngine:)), method_getTypeEncoding(exchangeM));
//这样就拿到了eatWithPersonName方法的type encodings 用作class_addMethod的第四个参数
        return YES; 
    } 
    return [super resolveInstanceMethod:sel];
}

- (void)startEngine:(NSString *)brand { 
    NSLog(@"my %@ car starts the engine", brand);
}

其中 class_getMethodImplementation 意思就是获取 SEL 的具体实现的指针(拿到方法eatWithPersonName的IMP指针)
然后创建一个新的类「DynamicSelector」,在这个新类中我们实现对「Car」的动态添加方法

参数解释:
cls 参数表示需要添加新方法的类
name 参数表示 selector 的方法名称
imp 即 implementation ,表示由编译器生成的、指向实现方法的指针。也就是说,这个指针指向的方法就是我们要添加的方法
最后一个参数 *types 表示我们要添加的方法的返回值和参数
char *types
比如:”v@:”意思就是这已是一个void类型的方法,没有参数传入。

再比如 “i@:”就是说这是一个int类型的方法,没有参数传入。

再再比如”i@:@”就是说这是一个int类型的方法,又一个参数传入

performSelector: withObject:是在iOS中的一种方法调用方式。他可以向一个对象传递任何消息,而不需要在编译的时候声明这些方法。
所以这也是runtime的一种应用方式.所以performSelector和直接调用方法的区别就在与runtime。直接调用编译是会自动校验。如果方法不存在,那么直接调用 在编译时候就能够发现,编译器会直接报错。
但是使用performSelector的话一定是在运行时候才能发现,如果此方法不存在就会崩溃。所以如果使用performSelector的话他就会有个最佳伴侣- (BOOL)respondsToSelector:(SEL)aSelector;来在运行时判断对象是否响应此方法

method相关的方法

//判断类中是否包含某个方法的实现
  BOOL class_respondsToSelector(Class cls, SEL sel)
  //获取类中的方法列表
  Method *class_copyMethodList(Class cls, unsigned int *outCount) 
  //为类添加新的方法,如果方法该方法已存在则返回NO
  BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
  //替换类中已有方法的实现,如果该方法不存在添加该方法
  IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types) 
  //获取类中的某个实例方法(减号方法)
  Method class_getInstanceMethod(Class cls, SEL name)
  //获取类中的某个类方法(加号方法)
  Method class_getClassMethod(Class cls, SEL name)
  //获取类中的方法实现
  IMP class_getMethodImplementation(Class cls, SEL name)
  //获取类中的方法的实现,该方法的返回值类型为struct
  IMP class_getMethodImplementation_stret(Class cls, SEL name) 

  //获取Method中的SEL
  SEL method_getName(Method m) 
  //获取Method中的IMP
  IMP method_getImplementation(Method m)
  //获取方法的Type字符串(包含参数类型和返回值类型)
  const char *method_getTypeEncoding(Method m) 
  //获取参数个数
  unsigned int method_getNumberOfArguments(Method m)
  //获取返回值类型字符串
  char *method_copyReturnType(Method m)
  //获取方法中第n个参数的Type
  char *method_copyArgumentType(Method m, unsigned int index)
  //获取Method的描述
  struct objc_method_description *method_getDescription(Method m)
  //设置Method的IMP
  IMP method_setImplementation(Method m, IMP imp) 
  //替换Method
  void method_exchangeImplementations(Method m1, Method m2)

  //获取SEL的名称
  const char *sel_getName(SEL sel)
  //注册一个SEL
  SEL sel_registerName(const char *str)
  //判断两个SEL对象是否相同
  BOOL sel_isEqual(SEL lhs, SEL rhs) 

  //通过块创建函数指针,block的形式为^ReturnType(id self,参数,...)
  IMP imp_implementationWithBlock(id block)
  //获取IMP中的block
  id imp_getBlock(IMP anImp)
  //移出IMP中的block
  BOOL imp_removeBlock(IMP anImp)

  //调用target对象的sel方法
  id objc_msgSend(id target, SEL sel, 参数列表...)

实际使用


@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    Person *p = [[Person alloc] init];

    // 默认person,没有实现eat方法,可以通过performSelector调用,但是会报错。
    // 动态添加方法就不会报错
    [p performSelector:@selector(eat)];

}


@end


@implementation Person
// void(*)()
// 默认方法都有两个隐式参数,
void eat(id self,SEL sel)
{
    NSLog(@"%@ %@",self,NSStringFromSelector(sel));
}

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

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

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

    }

    return [super resolveInstanceMethod:sel];
}
@end

备注:有些文字来自网友的帖子,本帖用于记录学习过程

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

推荐阅读更多精彩内容