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;
}
/* 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);
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
块操作
我们都知道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!
|