玩转iOS开发:装逼技术RunTime的应用(一)
文章分享至我的个人技术博客:cainrun.github.io/15065147177…
前面我们把RunTime
的一些基本知识都了解了一遍, 知道了在Objective-C
的方法调用是属于消息传送的机制.
接着呢, 我们知道了每个类都有一个isa
的结构体指针, 在这个结构体里, 我们得到指定类的所有属性, 所有方法的列表, 也可以知道这个所属的父类是什么等等的.
这只是RunTime
黑魔法的一丢丢应用, 如果没有看过之前那些文章的朋友可以去这里看看:
- 玩转iOS开发:iOS开发中的装逼技术 - RunTime(一)
- 玩转iOS开发:iOS开发中的装逼技术 - RunTime(二)
- 玩转iOS开发:iOS开发中的装逼技术 - RunTime(三)
转载声明:如需要转载该文章, 请联系作者, 并且注明出处, 以及不能擅自修改本文.
RunTime中的消息应用
在之前的文章里, 我们就有接触过RunTime
的消息机制, 通过Clang
把Object.m
文件转成Object.mm
文件, 然后就可以看得到里面的所有东西, 包括是怎么调用方法的也可以明确的看到.
这次我们换一个方式来实现, 首先我们声明一个类, 内部实现两个小方法:
#import "RunTimeMessageModel.h" @implementation RunTimeMessageModel - (void)cl_post { NSLog(@"被调用了: %@, 当前对象为: %@", NSStringFromClass([self class]), self); } - (void)cl_getWithCount:(NSInteger)count { NSLog(@"被%ld人调用了", count); } @end
在这里我们还需要修改一点东西, 不然我们没法用RunTime
的消息机制:
搞定完一切之后, 我们就来实现一下:
#import "RunTimeMessageController.h" #import "RunTimeMessageModel.h" #import <objc/message.h> @interface RunTimeMessageController () @end @implementation RunTimeMessageController - (void)viewDidLoad { [super viewDidLoad]; self.title = NSStringFromClass([self class]); Class getClass = objc_getClass("RunTimeMessageModel"); NSLog(@"Get The Class is: %@", getClass); // Xcode 会自动屏蔽通过objc_msgSend创建对象, 我们可以去到工程里设置 // Build Setting -> Enable Strict Checking of objc_msgSend Calls 改成No就好了. RunTimeMessageModel *messageModel = objc_msgSend(getClass, @selector(alloc)); NSLog(@"alloc Object: %@", messageModel); // 在不调用init方法, 我们也可以通过发消息调用想用的方法, 这里调用没有在.h文件里声明的方法会警告该方法没有声明 objc_msgSend(messageModel, @selector(cl_post)); messageModel = objc_msgSend(messageModel, @selector(init)); NSLog(@"init Object: %@", messageModel); objc_msgSend(messageModel, @selector(cl_post)); // 还有另外一种写法, 就是把所有东西都集合在一起, 也就是我们常用的[[NSObject alloc] init];的原型 RunTimeMessageModel *messageModelTwo = objc_msgSend(objc_msgSend(objc_getClass("RunTimeMessageModel"), @selector(alloc)), @selector(init)); objc_msgSend(messageModelTwo, @selector(cl_getWithCount:), 5); } @end
打印结果:
2017-09-27 22:55:44.713329+0800 RunTimeExample[74324:4732411] -[RunTimeMessageController viewDidLoad] 第27行 Get The Class is: RunTimeMessageModel 2017-09-27 22:55:44.714726+0800 RunTimeExample[74324:4732411] -[RunTimeMessageController viewDidLoad] 第33行 alloc Object: <RunTimeMessageModel: 0x60400001b9f0> 2017-09-27 22:55:44.715881+0800 RunTimeExample[74324:4732411] -[RunTimeMessageModel cl_post] 第15行 被调用了: RunTimeMessageModel, 当前对象为: <RunTimeMessageModel: 0x60400001b9f0> 2017-09-27 22:55:44.716663+0800 RunTimeExample[74324:4732411] -[RunTimeMessageController viewDidLoad] 第40行 init Object: <RunTimeMessageModel: 0x60400001b9f0> 2017-09-27 22:55:44.718265+0800 RunTimeExample[74324:4732411] -[RunTimeMessageModel cl_post] 第15行 被调用了: RunTimeMessageModel, 当前对象为: <RunTimeMessageModel: 0x60400001b9f0> 2017-09-27 22:55:44.719543+0800 RunTimeExample[74324:4732411] -[RunTimeMessageModel cl_getWithCount:] 第20行 被5人调用了
由于之前的文章里已经有做过解释了, 这里就不详细讲解了.
RunTime方法交换
在这里还有比较有意思的用处, 就是交换两个方法, 这里另外建一个类:
// RunTimeMethodModel.h文件 #import <Foundation/Foundation.h> @interface RunTimeMethodModel : NSObject @property (nonatomic, copy) NSString *cl_height; @property (nonatomic, copy) NSString *cl_weight; - (NSString *)cl_height; - (NSString *)cl_weight; @end // RunTimeMethodModel.m文件 #import "RunTimeMethodModel.h" @implementation RunTimeMethodModel - (NSString *)cl_height { return @"我身高180"; } - (NSString *)cl_weight { return @"我体重280"; } @end
- (void)viewDidLoad { [super viewDidLoad]; self.title = NSStringFromClass([self class]); RunTimeMethodModel *methodModel = [[RunTimeMethodModel alloc] init]; NSLog(@"身高: %@", methodModel.cl_height); NSLog(@"体重: %@", methodModel.cl_weight); Method methodOne = class_getInstanceMethod([methodModel class], @selector(cl_height)); Method methodTwo = class_getInstanceMethod([methodModel class], @selector(cl_weight)); method_exchangeImplementations(methodOne, methodTwo); NSLog(@"打印的内容: %@", [methodModel cl_height]); }
打印结果:
2017-09-28 00:36:20.277653+0800 RunTimeExample[76955:4827313] -[RunTimeMethodController viewDidLoad] 第27行 身高: 我身高180 2017-09-28 00:36:20.279144+0800 RunTimeExample[76955:4827313] -[RunTimeMethodController viewDidLoad] 第28行 体重: 我体重280 2017-09-28 00:36:20.283090+0800 RunTimeExample[76955:4827313] -[RunTimeMethodController viewDidLoad] 第35行 打印的内容: 我体重280
PS: 但这里需要注意一点, 由于这里的ViewController
会销毁, 但method_exchangeImplementations
会一直存在, 再次进来的时候, 就会再次根据上次交换过的顺序再次交换.
那怎么办呢? 查了一下资料, 发现有两个解决的方案:
+load交换方法
我们可以把交换方法的步骤放在+load
, 试试看:
+ (void)load { Method methodOne = class_getInstanceMethod(self, @selector(cl_height)); Method methodTwo = class_getInstanceMethod(self, @selector(cl_weight)); method_exchangeImplementations(methodOne, methodTwo); } - (NSString *)cl_height { return @"我身高180"; } - (NSString *)cl_weight { return @"我体重280"; }
打印结果:
2017-09-30 20:32:48.054168+0800 RunTimeExample[81266:5241395] -[RunTimeMethodController viewDidLoad] 第23行 身高: 我体重280 2017-09-30 20:32:48.054436+0800 RunTimeExample[81266:5241395] -[RunTimeMethodController viewDidLoad] 第24行 体重: 我身高180 2017-09-30 20:33:19.179724+0800 RunTimeExample[81266:5241395] -[RunTimeMethodController viewDidLoad] 第23行 身高: 我体重280 2017-09-30 20:33:19.179947+0800 RunTimeExample[81266:5241395] -[RunTimeMethodController viewDidLoad] 第24行 体重: 我身高180
PS: 虽然在+load
这个方法里的确是可以保证方法交换只有一次, 但这里有一个弊端, 就是当程序一运行就会执行这个方法交换了, 这并不是一个好的方案.
+initialize交换方法
这里我们尝试第二个方案, 使用+initialize
方法:
+ (void)initialize { Method methodOne = class_getInstanceMethod(self, @selector(cl_height)); Method methodTwo = class_getInstanceMethod(self, @selector(cl_weight)); method_exchangeImplementations(methodOne, methodTwo); } - (NSString *)cl_height { return @"我身高180"; } - (NSString *)cl_weight { return @"我体重280"; }
打印结果:
2017-09-30 20:42:49.750880+0800 RunTimeExample[81385:5249133] -[RunTimeMethodController viewDidLoad] 第23行 身高: 我体重280 2017-09-30 20:42:49.752335+0800 RunTimeExample[81385:5249133] -[RunTimeMethodController viewDidLoad] 第24行 体重: 我身高180
ok, 这满足了我们的需要了, 这解释一下+load
和+initialize
的区别:
- +load: 程序一开始就会去执行, 只执行一次.
- +initialize: 当类被初始化的时候会才会去执行, 该类只会执行一次.
当然并不是说在+load
上用是不对的, 也不是说+initialize
就一定是对的, 根据场景的需要来使用才是王道.
RunTime方法拦截
从刚刚我们就知道, 可以使用method_exchangeImplementations
交换两个方法, 但只应用在本类, 现在我们来看看别的应用:
@implementation BaseModel - (void)cl_logBaseModel { NSLog(@"Base Model Log"); } @end
@implementation InterceptModel - (void)cl_logInterceptModel { NSLog(@"Intercept You Method "); } @end
最终的实现:
+ (void)initialize { Method mehtodOne = class_getInstanceMethod([BaseModel class], @selector(cl_logBaseModel)); Method mehtodTwo = class_getInstanceMethod([InterceptModel class], @selector(cl_logInterceptModel)); method_exchangeImplementations(mehtodOne, mehtodTwo); } - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; BaseModel *baseModel = [[BaseModel alloc] init]; [baseModel cl_logBaseModel]; }
打印结果:
2017-10-01 12:36:03.488764+0800 RunTimeExample[82538:5345309] -[InterceptModel cl_logInterceptModel] 第15行 Intercept You Method
发现方法是被InterceptModel
这个类拦截, 并且替换了InterceptModel
的方法.
补充点小知识
这里我们都是用实例方法来作为例子, 那是不是说只能使用实例方法呢?
其实并不是的, 类方法也可以交换和拦截:
#import "BaseModel.h" @implementation BaseModel - (void)cl_logBaseModel { NSLog(@"Base Model Log"); } + (void)cl_logBaseModelClass { NSLog(@"Base Model Class Log"); } @end
@implementation InterceptModel - (void)cl_logInterceptModel { NSLog(@"Intercept You Method "); } + (void)cl_logInterceptModelClass { NSLog(@"Intercept Class You Method "); } @end
最终实现:
+ (void)initialize { // 拦截实例方法 Method mehtodOne = class_getInstanceMethod([BaseModel class], @selector(cl_logBaseModel)); Method mehtodTwo = class_getInstanceMethod([InterceptModel class], @selector(cl_logInterceptModel)); method_exchangeImplementations(mehtodOne, mehtodTwo); // 拦截类方法 Method classMehtodOne = class_getClassMethod([BaseModel class], @selector(cl_logBaseModelClass)); Method classMehtodTwo = class_getClassMethod([InterceptModel class], @selector(cl_logInterceptModelClass)); method_exchangeImplementations(classMehtodOne, classMehtodTwo); } - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; BaseModel *baseModel = [[BaseModel alloc] init]; [baseModel cl_logBaseModel]; [BaseModel cl_logBaseModelClass]; }
2017-10-01 12:59:13.229912+0800 RunTimeExample[82996:5374475] -[InterceptModel cl_logInterceptModel] 第15行 Intercept You Method 2017-10-01 12:59:13.230480+0800 RunTimeExample[82996:5374475] +[InterceptModel cl_logInterceptModelClass] 第20行 Intercept Class You Method
工程地址
项目地址: github.com/CainRun/iOS…
最后
相关推荐
hive运行在hadoop基础上。选择一个hadoop服务器、安装hadoop。connect jdbc:hive2://<host>:<port>/<db>;auth=noSasl root 123