首先感谢祖国,可以无忧无虑的码代码 ~
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
是一个特定的名称,即选择器SEL
,key
对应着一个实现,即IMP
指向底层C
函数的指针
1. SEL
SEL
(objc/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
是一个指向方法的指针,是一个根据方法名hash
的key
值,能唯一代表一个方法,可以加快方法的查询速度
那么怎么获取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
实际上是一个指向函数指针,指向方法实现的首地址,默认会当做无参数无返回指的函数,如果需要识别参数的话,需要将如下配置设置为NO
(TARGETS -> 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
method
(objc/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
,相当于在SEL
和IMP
之前做了映射,有了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
提供了一系列方法来处理与方法相关的操作
方法相关的函数
如果需要识别参数的话,需要将如下配置设置为NO
(TARGETS -> 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