runtime實戰(二)動態新增方法
阿新 • • 發佈:2018-11-01
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, "[email protected]:");
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