runtime_02

首先感谢祖国,可以无忧无虑的码代码 ~


If I have seen further, it is by standing on the shoudlers of giants.


  • SEL
    用于在运行时表示一个方法的名字,一个方法选择器就是一个C字符串,在OC运行时被注册。选择器由编译器生成,并且在类被加载的时候由运行时自动做映射操作
  • IMP
    这是一个指针类型,指向方法实现函数的开始位置,这个函数使用当前CPU架构实现的标准C调用规范,第一个参数是指向对象自身的指针self,第二个参数是方法选择器sel,然后是方法的实际参数
  • Method
    在类定义中表示方法的类型

一个类维护一个运行时可以接受的消息分发表,分发表中的每个入口是一个方法Method,其中key是一个特定的名称,即选择器SELkey对应着一个实现,即IMP指向底层C函数的指针


1. SEL

SELobjc/objc.h)又叫做选择器,表示方法的selector的指针

/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;

在源码中没有找到objc_selector相关的定义
方法的selector用于在运行时表示方法的名字,在编译的时候会依据每一个方法的名字、参数序列 生成一个唯一的整形标识 int型地址,这个标识就是SEL
eg:

SEL sel = @selector(viewDidAppear:);
NSLog(@"%p", sel);
// 0x102f5defd

两个类之间无论是否有关系,相同的方法只能对应一个SEL,即只要方法名相同,那么该方法的SEL就相同,每一个方法都对应一个SEL,所以在同一个类或者类的继承体系中,不能存在两个同名的方法,即时参数类型不同也不行。
不同得类可以拥有相同的selector,因为不同类的实例对象在执行相同的selector的时候,会在各自的方法列表中依据selector去寻找对应的方法实现IMP
eg:
同一个类中,这样编译器会报错,Duplicate declaration of method 'doSomethingWithTools:'

- (void)doSomethingWithTools:(NSArray *)tool;
- (void)doSomethingWithTools:(NSMutableArray *)tools;

本质上SEL是一个指向方法的指针,是一个根据方法名hashkey值,能唯一代表一个方法,可以加快方法的查询速度
那么怎么获取SEL

  • 使用sel_registerName
    runtime中注册一个方法,将方法名映射到一个选择器上,并返回这个选择器
/** 
 * Registers a method with the Objective-C runtime system, maps the method 
 * name to a selector, and returns the selector value.
 * 
 * @param str A pointer to a C string. Pass the name of the method you wish to register.
 * 
 * @return A pointer of type SEL specifying the selector for the named method.
 * 
 * @note You must register a method name with the Objective-C runtime system to obtain the
 *  method’s selector before you can add the method to a class definition. If the method name
 *  has already been registered, this function simply returns the selector.
 */
OBJC_EXPORT SEL _Nonnull
sel_registerName(const char * _Nonnull str)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
  • OC编译器提供的@selector()
  • NSSelectorFromString()NSObjCRuntime.h
FOUNDATION_EXPORT SEL NSSelectorFromString(NSString *aSelectorName);

其他方法

  • sel_getName
    选择选择器的名字
/** 
 * Returns the name of the method specified by a given selector.
 * 
 * @param sel A pointer of type \c SEL. Pass the selector whose name you wish to determine.
 * 
 * @return A C string indicating the name of the selector.
 */
OBJC_EXPORT const char * _Nonnull
sel_getName(SEL _Nonnull sel)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
  • sel_isEqual
    比较两个选择器
/** 
 * Returns a Boolean value that indicates whether two selectors are equal.
 * 
 * @param lhs The selector to compare with rhs.
 * @param rhs The selector to compare with lhs.
 * 
 * @return \c YES if \e lhs and \e rhs are equal, otherwise \c NO.
 * 
 * @note sel_isEqual is equivalent to ==.
 */
OBJC_EXPORT BOOL
sel_isEqual(SEL _Nonnull lhs, SEL _Nonnull rhs) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
2. IMP

IMP实际上是一个指向函数指针,指向方法实现的首地址,默认会当做无参数无返回指的函数,如果需要识别参数的话,需要将如下配置设置为NOTARGETS -> Build Settings -> Enable Strict Checking of objc_msgSend Calls

/// A pointer to the function of a method implementation. 
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ ); 
#else
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); 
#endif

第一个参数是指向self的指针,如果是实例方法是类实例的内存地址,如果是类方法是指向元类的指针;第二个参数是方法选择器SEL;然后是方法的实际参数列表
这个函数使用的是当前CPU架构实现的标准C的调用约定
SEL就是为了查找方法的最终实现IMP的,由于每个方法对应一个唯一的SEL,所以可以通过SEL快速获取对应的IMP,然后就获得了执行该方法的入口点,就可以像调用普通C函数的一样来使用这个函数指针了
而且通过取得的IMP,可以跳过runtime的消息传递机制,直接执行IMP指向的函数实现,这样就省去了runtime消息传递过程中所做的一系列查找,比直接向对象发送消息效率更高

3. Method

methodobjc/runtime.h)用于表示类定义中的方法,

/// An opaque type that represents a method in a class definition.
typedef struct objc_method *Method;

struct objc_method {
    SEL _Nonnull method_name                                 OBJC2_UNAVAILABLE;
    char * _Nullable method_types                            OBJC2_UNAVAILABLE;
    IMP _Nonnull method_imp                                  OBJC2_UNAVAILABLE;
}                                                            OBJC2_UNAVAILABLE;

结构体objc_method包含一个SEL和一个IMP,相当于在SELIMP之前做了映射,有了SEL就可以找到对应的IMP,从而调用方法的实现代码
objc_method_description定义了一个方法

/// Defines a method
struct objc_method_description {
    SEL _Nullable name;               /**< The name of the method */
    char * _Nullable types;           /**< The types of the method arguments */
};

runtime提供了一系列方法来处理与方法相关的操作
方法相关的函数
如果需要识别参数的话,需要将如下配置设置为NOTARGETS -> Build Settings -> Enable Strict Checking of objc_msgSend Calls

  • method_invoke
    调用指定的方法实现,返回的是实际实现的返回值,参数receiver不能为空
  • methd_invoke_stret
    返回一个方法数据结构的实现
/* Direct Method Invocation Primitives
 * Use these functions to call the implementation of a given Method.
 * This is faster than calling method_getImplementation() and method_getName().
 *
 * The receiver must not be nil.
 *
 * These functions must be cast to an appropriate function pointer type 
 * before being called. 
 */
#if !OBJC_OLD_DISPATCH_PROTOTYPES
OBJC_EXPORT void
method_invoke(void /* id receiver, Method m, ... */ ) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

OBJC_EXPORT void
method_invoke_stret(void /* id receiver, Method m, ... */ ) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0)
    OBJC_ARM64_UNAVAILABLE;
#else
OBJC_EXPORT id _Nullable
method_invoke(id _Nullable receiver, Method _Nonnull m, ...) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

OBJC_EXPORT void
method_invoke_stret(id _Nullable receiver, Method _Nonnull m, ...) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0)
    OBJC_ARM64_UNAVAILABLE;
#endif
  • method_getName
    获取方法的名称
/** 
 * Returns the name of a method.
 * 
 * @param m The method to inspect.
 * 
 * @return A pointer of type SEL.
 * 
 * @note To get the method name as a C string, call \c sel_getName(method_getName(method)).
 */
OBJC_EXPORT SEL _Nonnull
method_getName(Method _Nonnull m) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

eg:

Method method = class_getInstanceMethod([FFSummerModel class], @selector(shouldGoSwimming));
SEL selsel = method_getName(method); // sel_getName(selsel)
NSLog(@"%@", NSStringFromSelector(selsel));
// shouldGoSwimming
  • method_getImplementation
    返回方法的实现
/** 
 * Returns the implementation of a method.
 * 
 * @param m The method to inspect.
 * 
 * @return A function pointer of type IMP.
 */
OBJC_EXPORT IMP _Nonnull
method_getImplementation(Method _Nonnull m) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
  • method_getTypeEncoding
    获取描述方法的参数和返回值类型编码的字符串
/** 
 * Returns a string describing a method's parameter and return types.
 * 
 * @param m The method to inspect.
 * 
 * @return A C string. The string may be \c NULL.
 */
OBJC_EXPORT const char * _Nullable
method_getTypeEncoding(Method _Nonnull m) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
  • method_copyReturnType
    获取方法返回值类型字符串
/** 
 * Returns a string describing a method's return type.
 * 
 * @param m The method to inspect.
 * 
 * @return A C string describing the return type. You must free the string with \c free().
 */
OBJC_EXPORT char * _Nonnull
method_copyReturnType(Method _Nonnull m) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
  • method_copyArgumentType
    获取方法指定位置参数类型的字符串
/** 
 * Returns a string describing a single parameter type of a method.
 * 
 * @param m The method to inspect.
 * @param index The index of the parameter to inspect.
 * 
 * @return A C string describing the type of the parameter at index \e index, or \c NULL
 *  if method has no parameter index \e index. You must free the string with \c free().
 */
OBJC_EXPORT char * _Nullable
method_copyArgumentType(Method _Nonnull m, unsigned int index) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
  • method_getReturnType
    通过引用方式返回 方法的返回值类型的字符串
/** 
 * Returns by reference a string describing a method's return type.
 * 
 * @param m The method you want to inquire about. 
 * @param dst The reference string to store the description.
 * @param dst_len The maximum number of characters that can be stored in \e dst.
 *
 * @note The method's return type string is copied to \e dst.
 *  \e dst is filled as if \c strncpy(dst, parameter_type, dst_len) were called.
 */
OBJC_EXPORT void
method_getReturnType(Method _Nonnull m, char * _Nonnull dst, size_t dst_len) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
  • method_getNumberOfArguments
    返回 方法的参数的个数
/** 
 * Returns the number of arguments accepted by a method.
 * 
 * @param m A pointer to a \c Method data structure. Pass the method in question.
 * 
 * @return An integer containing the number of arguments accepted by the given method.
 */
OBJC_EXPORT unsigned int
method_getNumberOfArguments(Method _Nonnull m)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
  • method_getArgumentType
    通过引用方式 返回方法指定位置参数的类型字符串
/** 
 * Returns by reference a string describing a single parameter type of a method.
 * 
 * @param m The method you want to inquire about. 
 * @param index The index of the parameter you want to inquire about.
 * @param dst The reference string to store the description.
 * @param dst_len The maximum number of characters that can be stored in \e dst.
 * 
 * @note The parameter type string is copied to \e dst. \e dst is filled as if \c strncpy(dst, parameter_type, dst_len) 
 *  were called. If the method contains no parameter with that index, \e dst is filled as
 *  if \c strncpy(dst, "", dst_len) were called.
 */
OBJC_EXPORT void
method_getArgumentType(Method _Nonnull m, unsigned int index, 
                       char * _Nullable dst, size_t dst_len) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
  • method_getDescription
    返回 指定的 方法的 方法描述结构体
OBJC_EXPORT struct objc_method_description * _Nonnull
method_getDescription(Method _Nonnull m) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
  • method_setImplementation
    设置方法的实现,返回的是之前的实现
/** 
 * Sets the implementation of a method.
 * 
 * @param m The method for which to set an implementation.
 * @param imp The implemention to set to this method.
 * 
 * @return The previous implementation of the method.
 */
OBJC_EXPORT IMP _Nonnull
method_setImplementation(Method _Nonnull m, IMP _Nonnull imp) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
  • method_exchangeImplementations
    交换两个方法的实现
/** 
 * Exchanges the implementations of two methods.
 * 
 * @param m1 Method to exchange with second method.
 * @param m2 Method to exchange with first method.
 * 
 * @note This is an atomic version of the following:
 *  \code 
 *  IMP imp1 = method_getImplementation(m1);
 *  IMP imp2 = method_getImplementation(m2);
 *  method_setImplementation(m1, imp2);
 *  method_setImplementation(m2, imp1);
 *  \endcode
 */
OBJC_EXPORT void
method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

不定期更新 不合适的地方 还请指点~ 感激不尽
愿祖国繁荣昌盛~
o(* ̄3 ̄)o

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