runtime实战(二)动态添加方法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/shaohua_lv/article/details/71075467

runtime的实战二就是动态添加方法

一:创建一个Person类

我们在Person类的头文件声明一个eat方法,并不去实现它,当我们调用时,会出现如下的崩溃栈
2017-05-01 20:43:06.483 SH_Runtime[2133:104550] -[Person eat]: unrecognized selector sent to instance 0x618000018a20
2017-05-01 20:43:06.495 SH_Runtime[2133:104550] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Person eat]: unrecognized selector sent to instance 0x618000018a20'
*** First throw call stack:
(
    0   CoreFoundation                      0x0000000103f71d4b __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x00000001039d321e objc_exception_throw + 48
    2   CoreFoundation                      0x0000000103fe1f04 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
    3   CoreFoundation                      0x0000000103ef7005 ___forwarding___ + 1013
    4   CoreFoundation                      0x0000000103ef6b88 _CF_forwarding_prep_0 + 120
    5   SH_Runtime                          0x00000001033fc5f1 -[ViewController viewDidLoad] + 209
    6   UIKit                               0x0000000104537a3d -[UIViewController loadViewIfRequired] + 1258
    7   UIKit                               0x0000000104537e70 -[UIViewController view] + 27
    8   UIKit                               0x00000001044014b5 -[UIWindow addRootViewControllerViewIfPossible] + 71
    9   UIKit                               0x0000000104401c06 -[UIWindow _setHidden:forced:] + 293
    10  UIKit                               0x0000000104415519 -[UIWindow makeKeyAndVisible] + 42
    11  UIKit                               0x000000010438df8d -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 4818
    12  UIKit                               0x00000001043940ed -[UIApplication _runWithMainScene:transitionContext:completion:] + 1731
    13  UIKit                               0x000000010439126d -[UIApplication workspaceDidEndTransaction:] + 188
    14  FrontBoardServices                  0x000000010756d6cb __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 24
    15  FrontBoardServices                  0x000000010756d544 -[FBSSerialQueue _performNext] + 189
    16  FrontBoardServices                  0x000000010756d8cd -[FBSSerialQueue _performNextFromRunLoopSource] + 45
    17  CoreFoundation                      0x0000000103f16761 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    18  CoreFoundation                      0x0000000103efb98c __CFRunLoopDoSources0 + 556
    19  CoreFoundation                      0x0000000103efae76 __CFRunLoopRun + 918
    20  CoreFoundation                      0x0000000103efa884 CFRunLoopRunSpecific + 420
    21  UIKit                               0x000000010438faea -[UIApplication _run] + 434
    22  UIKit                               0x0000000104395c68 UIApplicationMain + 159
    23  SH_Runtime                          0x00000001033fc98f main + 111
    24  libdyld.dylib                       0x0000000106dd368d start + 1
    25  ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

注意:第三、第四行我们看到Terminating app due to uncaught exception ‘NSInvalidArgumentException’,这个是什么呢?
其实这是苹果给开发者的一个捕获异常的机会,NSObject类有方法:

// 捕获未实现的类方法
+ (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
// 捕获未实现的实例方法
+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);

在Person.m文件实现这两个方法,便实现动态添加的功能;

#import "Person.h"
#import <objc/runtime.h>

@implementation Person

// 捕获未实现的类方法
+ (BOOL)resolveClassMethod:(SEL)sel
{

    return NO;
}

// 捕获未实现的实例方法
+(BOOL)resolveInstanceMethod:(SEL)sel
{
    NSLog(@"the sel is :%@",NSStringFromSelector(sel));
    if (sel == @selector(eat)) {
        // runtime 添加方法;
        // class: 所要添加的类;
        // SEL: 方法编号;
        // IMP(Implementation):方法实现,一个方法指针
        // type: 类型
        class_addMethod([Person class], sel, (IMP)eat, "");
        return YES;
    }
    return NO;
}

void eat() {
    NSLog(@"person eat");
}

@end

另:class_addMethod方法的第四个参数一般情况为空字符串即代码没有参数和返回值,对于有返回值和参数方法,看苹果开发者文档:

Tables Are
Code Meaning
c A char
i An int
s A short
l A long l is treated as a 32-bit quantity on 64-bit programs.
q A long long
C An unsigned char
I An unsigned int
S An unsigned short
L An unsigned long
Q An unsigned long long
f A float
d A double
B A C++ bool or a C99 _Bool
v A void
* A character string (char *)
@ An object (whether statically typed or typed id)
# A class object (Class)
: A method selector (SEL)
[array type] An array
{name=type…} A structure
(name=type…) A union
bnum A bit field of num bits
^type A pointer to type
? An unknown type (among other things, this code is used for function pointers)

参阅Type Encoding表我们可知:

class_addMethod([self class], @selector(resolveThisMethodDynamically), (IMP) myMethodIMP, "v@:");

An array of characters that describe the types of the arguments to the
method. For possible values, see Objective-C Runtime Programming Guide
Type Encodings. Since the function must take at least two arguments—self and _cmd, the second and third characters must be “@:”
(the first character is the return type).

方法的返回值为void,而@:是必须的参数。

我的GitHub代码地址:https://github.com/lvshaohua/SH_Runtime

猜你喜欢

转载自blog.csdn.net/shaohua_lv/article/details/71075467