Runtime--Selector、IMP、Method

Selector

Selector的类型是SEL。能够用来唯一标识方法。能够像动态的函数指针一样精准的指向方法的implementation。

获取方法

在编译期,使用编译器指令@@selector,例如

SEL aSelector = @selector(methodName);

在运行期,使用NSSelectorFromString函数,例如

SEL aSelector = NSSelectorFromString(@"methodName");

使用方法

一下方法在NSObject类中声明

- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

与Runtime

首先定义一个方法

-(void)getMethod:(NSString *)name
{
    NSLog(@"getMethod:%@",name);
}

然后获取方法

SEL sel = @selector(getMethod:);
SEL sel2 = NSSelectorFromString(@"getMethod:");

获取方法名称

 //获取名称
const char *sel_name = sel_getName(sel);
NSLog(@"SEL sel_name:%@",[NSString stringWithUTF8String:sel_name]);

//输出
SEL sel_name:ivarMethod

注册方法

//在添加一个Method之前需要先注册一个;如果方法已注册直接返回
SEL sel_registerName(const char *str)

//或者
SEL sel_getUid(const char *str)

例如

//注册
SEL sel_regist = sel_registerName("sel_regist");
NSLog(@"SEL sel_regist:%@",NSStringFromSelector(sel_regist));

SEL sel_regist2 =sel_getUid("sel_regist");
NSLog(@"SEL sel_regist2:%@",NSStringFromSelector(sel_regist2));

//输出
SEL sel_regist:sel_regist
SEL sel_regist2:sel_regist

比较方法

//相等于符号 == 
BOOL sel_isEqual(SEL lhs, SEL rhs) 

例如

//比较
BOOL isEqual = sel_isEqual(sel_regist2, sel_regist2);
NSLog(@"SEL isEqual:%d",isEqual);
  
//输出
SEL isEqual:1    

IMP(Implementation)

一个指向实现Method的函数的开始的指针。此函数使用标准的C语言调用习惯来实现CGU架构。其实参数:self、selector、其它Method参数。其中实例方法而言,self指向对象的内存地址;对于类方法而言self指向其metaclass。

Method

此处讨论Method与Runtime相关的指示。首先定义一个简单的Data类,声明并实现一些方法

@interface Data : NSObject

-(void)getMethod:(NSString *)name;
-(void)ivarMethod;
+(void)classMethod;

@end


@implementation Data
-(void)ivarMethod
{
    NSLog(@"调用ivarMethod");
}
+(void)classMethod
{
    NSLog(@"调用classMethod");

}

-(void)getMethod:(NSString *)name
{
    NSLog(@"getMethod:%@",name);
}
@end

获取Method

注释:不管在header文件还是implement文件声明实现的方法均可以获取到。

获取实例Method

 Method ivar_Method = class_getInstanceMethod([Data class], @selector(ivarMethod));
 SEL sel_ivar_Method = method_getName(ivar_Method);
 NSLog(@"Method class_getInstanceMethod:%@",NSStringFromSelector(sel_ivar_Method));

//输出
class_getInstanceMethod:ivarMethod

获取类Method

/* 获取类方法 */
Method class_Method = class_getClassMethod([Data class], @selector(classMethod));
SEL sel_class_Method = method_getName(class_Method);
NSLog(@"Method class_getClassMethod:%@",NSStringFromSelector(sel_class_Method));

//输出
Method class_getClassMethod:classMethod    

获取实例Method列表

/** 
 * 获取类的实例方法列表
 * 
 * @return 以数组想法返回实例方法列表(需要free);只返回类本身实现的方法,不包括父类的继承的方法。 
 *   
 * 获取类方法列表: class_copyMethodList(object_getClass(cls), &count).
 * 获取父类方法:class_getInstanceMethod 或 class_getClassMethod.
 */
Method *class_copyMethodList(Class cls, unsigned int *outCount);
/* 获取实例方法列表 */
unsigned int count_method = 0;
Method *method_list = class_copyMethodList([Data class], &count_method);
for (int i = 0; i < count_method; i ++)
{
   Method method = method_list[i];
   SEL sel_Method = method_getName(method);
   NSLog(@"Method class_copyMethodList:%@",NSStringFromSelector(sel_Method));

}

//输出
Method class_copyMethodList:ivarMethod
Method class_copyMethodList:getMethod:

获取类Method列表

/* 获取实例方法列表 */
unsigned int count_method = 0;
Method *method_list = class_copyMethodList(object_getClass([Data class]), &count_method);
for (int i = 0; i < count_method; i ++)
{
   Method method = method_list[i];
   SEL sel_Method = method_getName(method);
   NSLog(@"Method class_copyMethodList:%@",NSStringFromSelector(sel_Method));

}

//输出
Method class_copyMethodList:classMethod

Method属性获取和操作

首先以实例方法为例子获取一个Method
Method ivar_Method = class_getInstanceMethod([Data class], @selector(getMethod:));

方法调用

/* 
 * This is faster than calling method_getImplementation() and method_getName().
 *
 * receiver 不能为空
 * 函数必须先转换成合适的函数指针,然后才能调用
 */
void method_invoke(void /* id receiver, Method m, ... */ ) ;

例如调用@selector(getMethod:)

((void(*)(id,Method,id))method_invoke)([[Data alloc] init], ivar_Method,@"方法调用");

//输出
getMethod:方法调用

例如调用@selector(ivarMethod)

Method ivar_Method = class_getInstanceMethod([Data class], @selector(ivarMethod));
((void(*)(id, Method))method_invoke)([[Data alloc] init], ivar_Method);

//输出
调用ivarMethod

例如调用类方法@selector(classMethod)

Method class_Method = class_getClassMethod([Data class], @selector(classMethod));
//object_getClass([Data class])原理是类方法是去Metaclass寻找的
((void(*)(id,Method))method_invoke)(object_getClass([Data class]),class_Method);

//输出
调用classMethod

关于函数指针的简单说明:
1、void 表示返回值为空
2、()表明是个函数指针
3、函数指针指向函数的内存地址
((void()(id,Method))method_invoke)**
4、函数指针的调用方式可以自行查阅资料

void add(int a,int b)
{
    int c = a+b;
    NSLog(@"%d",c);
    
}

//声明函数指针
void (*add2)(int,int);
//赋值
add2 = &add;
//调用
(*add2)(3,4);
((void(*)(int, int))add)(3, 4);
    
NSLog(@"%p--%p",&add,((void (*)(int, int))add));

//输出
7
7
0x107022970--0x107022970

获取名称

//获取名称
SEL sel_ivar_Method = method_getName(ivar_Method);
NSLog(@"Method class_getInstanceMethod:%@",NSStringFromSelector(sel_ivar_Method));

//输出
Method class_getInstanceMethod:getMethod:

获取IMP

//获取imp
IMP method_imp = method_getImplementation(ivar_Method);

获取返回值、参数类型

const char* method_TypeEncoding = method_getTypeEncoding(ivar_Method);
NSLog(@"method_TypeEncoding:%@",[NSString stringWithUTF8String:method_TypeEncoding]);

//输出
method_TypeEncoding:v24@0:8@16

获取返回值类型

//获取返回值类型
const char* method_ReturnType = method_copyReturnType(ivar_Method);
NSLog(@"method_ReturnType:%@",[NSString stringWithUTF8String:method_ReturnType]);
//C语言获取方法 method_getReturnType(<#Method m#>, <#char *dst#>, <#size_t dst_len#>)

//输出
method_ReturnType:v

获取参数个数、类型

//获取参数个数
unsigned int numberOfArguments = method_getNumberOfArguments(ivar_Method);
NSLog(@"numberOfArguments:%d",numberOfArguments);
    
//获取参数类型
unsigned int number = method_getNumberOfArguments(ivar_Method);
for ( int i = 0; i < number ; i ++)
{
   const char *type = method_copyArgumentType(ivar_Method, i);
   NSLog(@"ArgumentType:%@",[NSString stringWithUTF8String:type]);
}
 //C语言获取方法 method_getArgumentType(<#Method m#>, <#unsigned int index#>, <#char *dst#>, <#size_t dst_len#>)

//输出:@表示id类型 :表示Selector
numberOfArguments:3
ArgumentType:@
ArgumentType::
ArgumentType:@

获取描述

//获取描述
struct objc_method_description *method_descriptions = method_getDescription(ivar_Method);
struct objc_method_description method_description = method_descriptions[0];
SEL sel_des = method_description.name;
const char *type_des = method_description.types;
NSLog(@"objc_method_description:%@  types:%@",NSStringFromSelector(sel_des),[NSString stringWithUTF8String:type_des]);

//输出
objc_method_description:getMethod:  types:v24@0:8@16

设置IMP

IMP imp_new = class_getMethodImplementation([Data class], @selector(ivarMethod));
IMP imp_old = method_setImplementation(ivar_Method, imp_new);
((void(*)(id,Method,id))method_invoke)([[Data alloc] init], ivar_Method,@"方法调用");

//原来输出
getMethod:方法调用
//现在输出
调用ivarMethod

交换IMP

//交换
//用来交换的Method
Method ivar_Method2 = class_getInstanceMethod([Data class], @selector(ivarMethod));

//交换前调用
((void(*)(id,Method,id))method_invoke)([[Data alloc] init], ivar_Method,@"方法调用");
((void(*)(id,Method))method_invoke)([[Data alloc] init], ivar_Method2);
    
//交换
method_exchangeImplementations(ivar_Method, ivar_Method2);
    
//交换前调用
((void(*)(id,Method,id))method_invoke)([[Data alloc] init], ivar_Method,@"方法调用");
((void(*)(id,Method))method_invoke)([[Data alloc] init], ivar_Method2);

//输出结果
getMethod:方法调用
调用ivarMethod

调用ivarMethod
getMethod:<Data: 0x60800000d290>

添加Method

/** 
 * @param cls 目标类
 * @param name SEL
 * @param imp 新方法的实现函数,至少两个参数:self and _cmd.
 * @param types 参数类型列表  
 * 
 * @return 如果成功返回YES,否则NO(例如,已存在) 
 *  
 *
 * @note 可以覆盖父类方法,不改变自己已有方法(返回NO);改变已有方法:method_setImplementation。
 */
OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp, 
                                 const char *types) 

关于types的说明请参考Type Encodings,返回值类型+self+_cmd+其他参数s:v@:其他参数符号s

添加实例Method

//新增方法
-(void)ivarMethod2
{
    NSLog(@"ivarMethod2");
}

/* 添加方法 */
IMP imp_add = class_getMethodImplementation([self class], @selector(ivarMethod2));
BOOL isadd =  class_addMethod([Data class], @selector(ivarMethod2), imp_add, "v@:");
NSLog(@"isadd:%d",isadd);
    
/* 检测添加结果 */
unsigned int count_method = 0;
Method *method_list = class_copyMethodList([Data class], &count_method);
for (int i = 0; i < count_method; i ++)
{
   Method method = method_list[i];
   SEL sel_Method = method_getName(method);
   NSLog(@"Method class_copyMethodList:%@",NSStringFromSelector(sel_Method));
}

//输出结果
isadd:1
Method class_copyMethodList:ivarMethod2//添加成功
Method class_copyMethodList:getMethod:
Method class_copyMethodList:ivarMethod

添加类Method

//新增方法
+(void)classMethod2
{
    NSLog(@"classMethod2");
}

/* 添加类方法 */
IMP imp_add = class_getMethodImplementation([self class], @selector(classMethod2));
BOOL isadd =  class_addMethod(object_getClass([Data class]), @selector(classMethod2), imp_add, "v@:");
NSLog(@"isadd:%d",isadd);
    
/* 检测添加结果 */
unsigned int count_method = 0;
Method *method_list = class_copyMethodList(object_getClass([Data class]), &count_method);
for (int i = 0; i < count_method; i ++)
{
   Method method = method_list[i];
   SEL sel_Method = method_getName(method);
   NSLog(@"Method class_copyMethodList:%@",NSStringFromSelector(sel_Method));
}

//输出结果
isadd:1
Method class_copyMethodList:ivarMethod2
Method class_copyMethodList:classMethod2//添加成功
Method class_copyMethodList:classMethod

替换Method

/** 
 * 
 * @param cls 目标类
 * @param name SEL
 * @param imp 新方法的实现函数
 * @param types 参数类型列表 
 * @return 旧的IMP
 * 
 * @note 两种情况:
 *  - 如果方法不存在,执行 class_addMethod  
 *    
 *  - 如果方法存在,执行method_setImplementation
 *    
 */
OBJC_EXPORT IMP class_replaceMethod(Class cls, SEL name, IMP imp, 
                                    const char *types) 

例如

/* 替换方法 */    
Method ivar_Method = class_getInstanceMethod([Data class], @selector(ivarMethod));
    
//执行前
((void(*)(id,Method))method_invoke)([[Data alloc] init], ivar_Method);

//方法替换
IMP imp_new = class_getMethodImplementation([self class], @selector(ivarMethod2));
IMP imp_old = class_replaceMethod([Data class], @selector(ivarMethod), imp_new, "v@:");
    
//执行后
((void(*)(id,Method))method_invoke)([[Data alloc] init], ivar_Method);

//输出结果
调用ivarMethod//原来
ivarMethod2//交换后

是否响应

/* 响应 */
BOOL isResponse = class_respondsToSelector([Data class], @selector(ivarMethod));
NSLog(@"isResponse:%d",isResponse);

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

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,690评论 0 9
  • 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的...
    西木阅读 30,548评论 33 466
  • 继上Runtime梳理(四) 通过前面的学习,我们了解到Objective-C的动态特性:Objective-C不...
    小名一峰阅读 744评论 0 3
  • 本文转载自:http://yulingtianxia.com/blog/2014/11/05/objective-...
    ant_flex阅读 751评论 0 1
  • 参考链接: http://www.cnblogs.com/ioshe/p/5489086.html 简介 Runt...
    乐乐的简书阅读 2,132评论 0 9