Method IMP 概念介绍
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *_Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list *_Nullable *_Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache *_Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *_Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
然后我们可以看到:
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_method_list {
struct objc_method_list * _Nullable obsolete OBJC2_UNAVAILABLE;
int method_count OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
/* variable length structure */
struct objc_method method_list[1] OBJC2_UNAVAILABLE;
}
struct objc_method {
SEL _Nonnull method_name OBJC2_UNAVAILABLE;
char * _Nullable method_types OBJC2_UNAVAILABLE;
IMP _Nonnull method_imp OBJC2_UNAVAILABLE;
}
Runtime 关于Method IMP 的API介绍
/* Working with Methods */
/**
* 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);
/**
* 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);
/**
* 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);
/**
* 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 unsignedint
method_getNumberOfArguments(Method _Nonnull m)
OBJC_AVAILABLE(10.0,2.0,9.0,1.0,2.0);
/**
* 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);
/**
* 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);
/**
* 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);
/**
* 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);
OBJC_EXPORT struct objc_method_description *_Nonnull
method_getDescription(Method _Nonnull m)
OBJC_AVAILABLE(10.5,2.0,9.0,1.0,2.0);
/**
* 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);
/**
* 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 _Null_unspecified /* _Nonnull */ m1, Method _Null_unspecified /* _Nonnull */ m2)
OBJC_AVAILABLE(10.5,2.0,9.0,1.0,2.0);
Runtime 关于Method IMP 的应用示例
method_getName(Method _Nonnull m)
method_getImplementation(Method _Nonnull m)
- (void)viewDidLoad { [super viewDidLoad]; //首先我们早类里面找到该方法 Method ori_Method = class_getInstanceMethod([self class], @selector(myMethod:)); #if 0 //获取方法名 SEL SEL oriMethodName = method_getName(ori_Method); //根据方法名获取函数指针 IMP myMethodImp = [self methodForSelector:oriMethodName]; #else //直接根据方法名获取函数指针 等同于IF 0 IMP myMethodImp = method_getImplementation(ori_Method); #endif //在该类中添加方法。 #if 0 class_addMethod([self class], @selector(testMethod:), myMethodImp, method_getTypeEncoding(ori_Method)); #else class_addMethod([self class], @selector(testMethod:), myMethodImp, "V@:@"); #endif //在执行方法。 [self performSelector:@selector(testMethod:) withObject:@"7777"]; } -(void)myMethod:(NSString *)myValue{ NSLog(@"myMethod:%@",myValue); }
2017-07-20 10:52:26.581146+0800 zyTest[8351:416437] myMethod:7777
中间关于获得方法参数,参数个数,参数返回类型就不赘述了,示例中有的会用到。主要介绍下面几个方法:
上面是GET IMP,那肯定可以设置方法的IMP
method_setImplementation(Method _Nonnull m, IMP _Nonnull imp)
方法交换
method_exchangeImplementations(Method _Null_unspecified /* _Nonnull */ m1, Method _Null_unspecified /* _Nonnull */ m2)
//方法交换 -(void)demo2{ Method method1 = class_getInstanceMethod([self class], @selector(exchangeMethod1:)); Method method2 = class_getInstanceMethod([self class], @selector(exchangeMethod2:)); Method method3 = class_getInstanceMethod([self class], @selector(exchangeMethod3:)); Method method4 = class_getInstanceMethod([self class], @selector(exchangeMethod4:)); //方法替换 替换SEL 的IMP实现 class_replaceMethod([self class], @selector(exchangeMethod1:), method_getImplementation(method3), method_getTypeEncoding(method3)); //和class_replaceMethod 类似,替换method 的结构题IMP指针 method_setImplementation(method4, method_getImplementation(method2)); //方法交换 method_exchangeImplementations(method1, method2); //猜一猜打印的什么 [self performSelector:method_getName(method1) withObject:@"Runtime Method Demo1" afterDelay:0.0]; [self exchangeMethod2:@"Runtime Method Demo2"]; [self exchangeMethod3:@"Runtime Method Demo3"]; [self exchangeMethod4:@"Runtime Method Demo4"]; } -(void)exchangeMethod1:(id)str{ NSLog(@"exchangeMethod1:%@",str); } -(void)exchangeMethod2:(id)num{ NSLog(@"exchangeMethod2:%@",num); } -(void)exchangeMethod3:(id)f{ NSLog(@"exchangeMethod3:%@",f); } -(void)exchangeMethod4:(id)w{ NSLog(@"exchangeMethod4:%@",w); }
最后的打印结果是你想的吗?
2017-07-20 13:23:14.804342+0800 zyTest[16764:910790] exchangeMethod3:Runtime Method Demo2
2017-07-20 13:23:14.804586+0800 zyTest[16764:910790] exchangeMethod3:Runtime Method Demo3
2017-07-20 13:23:14.804738+0800 zyTest[16764:910790] exchangeMethod2:Runtime Method Demo4
2017-07-20 13:23:14.817341+0800 zyTest[16764:910790] exchangeMethod2:Runtime Method Demo1
Runtime IMP 块操作
块操作
我们都知道block给我们带到极大的方便,苹果也不断提供一些使用block的新的API。同时,苹果在runtime中也提供了一些函数来支持针对block的操作,这些函数包括:
1
2
3
4
5
6
7
8
|
// 创建一个指针函数的指针,该函数调用时会调用特定的block IMP imp_implementationWithBlock ( id block ); // 返回与IMP(使用imp_implementationWithBlock创建的)相关的block id imp_getBlock ( IMP anImp ); // 解除block与IMP(使用imp_implementationWithBlock创建的)的关联关系,并释放block的拷贝 BOOL imp_removeBlock ( IMP anImp ); |
● imp_implementationWithBlock函数:参数block的签名必须是method_return_type ^(id self, method_args …)形式的。该方法能让我们使用block作为IMP。如下代码所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@interface MyRuntimeBlock : NSObject @end @implementation MyRuntimeBlock @end // 测试代码 IMP imp = imp_implementationWithBlock(^(id obj, NSString *str) { NSLog(@ "%@" , str); }); class_addMethod(MyRuntimeBlock.class, @selector(testBlock:), imp, "v@:@" ); MyRuntimeBlock *runtime = [[MyRuntimeBlock alloc] init]; [runtime performSelector:@selector(testBlock:) withObject:@ "hello world!" ]; |
输出结果是
1
|
2014-11-09 14:03:19.779 [1172:395446] hello world! |