赞
踩
NSLog(@"SHOW TIME");
Method-Swizzling实际就是更换方法所对应的实现函数,如下图1-1,更换前调用方法selector1执行的是IMP1函数,更换后调用selector1执行的就变成了IMP2。
图1-1 method-swizzling更换两个selector方法的实现
2.1 首先创建个继承NSObject的类,.h文件声明两个实例方法test1、test2:
- @interface MethodSwizzling : NSObject
-
- -(void)test1;
- -(void)test2;
-
- @end
2.2 在.m文件中实现这两个方法,并交换两个方法的实现:
- #import "MethodSwizzling.h"
- #import <objc/runtime.h>
-
- @implementation MethodSwizzling
-
- + (void)load {
- // 获取test1、test2方法
- Method method_test1 = class_getInstanceMethod(self, @selector(test1));
- Method method_test2 = class_getInstanceMethod(self, @selector(test2));
- // 交换两个方法的实现
- method_exchangeImplementations(method_test1, method_test2);
- }
-
- -(void)test1 {
- NSLog(@"test1");
- }
-
- -(void)test2 {
- NSLog(@"test2");
- }
-
- @end
2.3 其他地方调用test1方法:
- MethodSwizzling *obj = [[MethodSwizzling alloc] init];
- [obj test1];
2.4 输出结果为@“test2”:
2020-08-06 18:30:08.966946+0800 RuntimeTest[28219:4174853] test2
由此可知,在方法交换后,调用test1方法,实际执行的函数是test2。这就是黑魔法Method-Swizzling的作用:更换实际执行的方法函数。
2.5 如果此时在test2方法的输出语句前调用test2[self test2],结果又会是什么呢?
- -(void)test2 {
- // 实际调用test1函数
- [self test2];
-
- NSLog(@"test2");
- }
2.6 输出结果为@“test1”、@“test2”:
- 2020-08-06 19:23:35.309874+0800 RuntimeTest[29953:4227972] test1
- 2020-08-06 19:23:35.310124+0800 RuntimeTest[29953:4227972] test2
小朋友,你是否有很多的问号???
我们来分析一波:
① 当程序考试运行时就会调用+(void)load方法,调用时机就不在这里解释了,在load方法内对两个方法函数进行交换;
② 通过[obj test1]调用test1方法,但此时test1方法的实际操作函数已经在①步骤中被调换到test2方法,所以[obj test1]实际走的是-(void)test2方法;
③ 此时test2方法内部执行[self test2],并不会造成循环,因为此时的test2方法的函数实现已经被调换指向了test1方法,所以会执行-(void)test1方法,输出@"test1"字符;
④ 最后调用test2方法的NSLog,输出@"test2"字符,执行完毕。
.h文件
- #import <UIKit/UIKit.h>
-
- NS_ASSUME_NONNULL_BEGIN
-
- @interface UIButton (QuickClick)
- @property (nonatomic,assign) NSTimeInterval delayTime;
- @end
-
- NS_ASSUME_NONNULL_END
.m文件
- #import "UIButton+QuickClick.h"
- #import <objc/runtime.h>
-
- @implementation UIButton (QuickClick)
- static const char* delayTime_str = "delayTime_str";
-
- + (void)load
- {
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- Method originMethod = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
- Method replacedMethod = class_getInstanceMethod(self, @selector(miSendAction:to:forEvent:));
- method_exchangeImplementations(originMethod, replacedMethod);
- });
- }
-
- - (void)miSendAction:(nonnull SEL)action to:(id)target forEvent:(UIEvent *)event
- {
- if (self.delayTime > 0) {
- if (self.userInteractionEnabled) {
- [self miSendAction:action to:target forEvent:event];
- }
- self.userInteractionEnabled = NO;
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
- (int64_t)(self.delayTime * NSEC_PER_SEC)),
- dispatch_get_main_queue(), ^{
- self.userInteractionEnabled = YES;
- });
- }else{
- [self miSendAction:action to:target forEvent:event];
- }
- }
-
- - (NSTimeInterval)delayTime
- {
- return [objc_getAssociatedObject(self, delayTime_str) doubleValue];
- }
-
- - (void)setDelayTime:(NSTimeInterval)delayTime
- {
- objc_setAssociatedObject(self, delayTime_str, @(delayTime), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
-
- @end
我们可以用method swizzling修改-[__NSDictionaryM setObject:forKey:]
方法,让它在设值时,先判断是否value为空,为空则不设置。代码如下:
- @implementation NSMutableDictionary (Safe)
-
- + (void)load {
- Class dictCls = NSClassFromString(@"__NSDictionaryM");
- Method originalMethod = class_getInstanceMethod(dictCls, @selector(setObject:forKey:));
- Method swizzledMethod = class_getInstanceMethod(dictCls, @selector(na_setObject:forKey:));
- method_exchangeImplementations(originalMethod, swizzledMethod);
- }
-
- - (void)na_setObject:(id)anObject forKey:(id<NSCopying>)aKey {
- if (!anObject)
- return;
- [self na_setObject:anObject forKey:aKey];
- }
-
- @end
- @implementation NSArray (Safe)
-
- + (void)load {
- Method originalMethod = class_getClassMethod(self, @selector(arrayWithObjects:count:));
- Method swizzledMethod = class_getClassMethod(self, @selector(na_arrayWithObjects:count:));
- method_exchangeImplementations(originalMethod, swizzledMethod);
- }
-
- + (instancetype)na_arrayWithObjects:(const id [])objects count:(NSUInteger)cnt {
- id nObjects[cnt];
- int i=0, j=0;
- for (; i<cnt && j<cnt; i++) {
- if (objects[i]) {
- nObjects[j] = objects[i];
- j++;
- }
- }
-
- return [self na_arrayWithObjects:nObjects count:j];
- }
- @end
-
- @implementation NSMutableArray (Safe)
-
- + (void)load {
- Class arrayCls = NSClassFromString(@"__NSArrayM");
-
- Method originalMethod1 = class_getInstanceMethod(arrayCls, @selector(insertObject:atIndex:));
- Method swizzledMethod1 = class_getInstanceMethod(arrayCls, @selector(na_insertObject:atIndex:));
- method_exchangeImplementations(originalMethod1, swizzledMethod1);
-
- Method originalMethod2 = class_getInstanceMethod(arrayCls, @selector(setObject:atIndex:));
- Method swizzledMethod2 = class_getInstanceMethod(arrayCls, @selector(na_setObject:atIndex:));
- method_exchangeImplementations(originalMethod2, swizzledMethod2);
- }
-
- - (void)na_insertObject:(id)anObject atIndex:(NSUInteger)index {
- if (!anObject)
- return;
- [self na_insertObject:anObject atIndex:index];
- }
-
- - (void)na_setObject:(id)anObject atIndex:(NSUInteger)index {
- if (!anObject)
- return;
- [self na_setObject:anObject atIndex:index];
- }
-
- @end
NSLog(@"END...");
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。