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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。