神奇的load方法
来源:互联网 发布:类似淘宝联盟有返利的 编辑:程序博客网 时间:2024/06/05 04:17
- load方法说明
- load方法的妙用
- 简化AppDelegate类
- 改进前
- 改进后
- 埋点统计
- load方法与initialize方法
- 注意事项
- 简化AppDelegate类
load方法说明
在Objective-C中,绝大多数类都继承自NSObject这个根类,而该类有load方法,可以用来实现初始化操作。其原型如下:
+ (void)load
对于加入运行期系统中的每个类(class)及分类(category)来说,必定会调用此方法,而且仅调用一次。当包含类或分类的程序库载入系统时,就会执行此方法(通常指应用程序启动)。如程序是iOS平台设计的,则肯定会在此时执行。Mac OS X应用程序更自由一些,它们可以使用“动态加载”(dynamic loading)之类的特性,等应用程序启动好之后再去加载程序库。如果分类和其所属的类都定义了load方法,则先调用类里的,再调用分类里的。
执行load方法时,运行期系统处于“脆弱状态”。在执行子类的load方法之前,必定会先执行所有超类的load方法,而如果代码还依赖了其他程序库,那么程序库里相关类的load方法也必定会先执行。
load方法的妙用
简化AppDelegate类
随着项目功能的不断增加,我们有很多功能或者第三库需要启动项目时就加载,AppDelegate类就会越来越庞大。这样结构既不够清晰,而且耦合性比较强。
改进前:
//设置NUI配置 [self setNUIConfig]; //开启统计 [MobClick startWithConfigure:UMConfigInstance]; //初始化数据库 [BYDBUtils startInitDB]; //注册统计平台 if (!TARGET_IPHONE_SIMULATOR) { [[SocialService sharedInstance] registerPlatforms]; } //检测服务器状态 [BYServerMgr sharedInstance] doGetServerStatus]; //获取用户数据 [USER_MGR updateUserAssets]; //启动图界面 BYLaunchVC *splashVC = [[KSLaunchVC alloc] initWithNibName:@"BYLaunchVC" bundle:nil]; UIWindow *keywindow = [UIApplication sharedApplication].keyWindow; [keywindow addSubview:splashVC.view]; [keywindow bringSubviewToFront:splashVC.view]; [self.window makeKeyAndVisible]; //自适应屏幕键盘控件 IQKeyboardManager * manager = [IQKeyboardManager sharedManager]; manager.enable = YES; manager.shouldResignOnTouchOutside = YES; manager.shouldToolbarUsesTextFieldTintColor = YES; manager.enableAutoToolbar = YES; //设置首页 BYCircleListViewController *homePageVC = [[BYCircleListViewController alloc] init]; BYNavigationViewController *navVC = [[BYNavigationViewController alloc] initWithRootViewController:homePageVC]; self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; self.window.rootViewController = navVC; [self.window makeKeyAndVisible];
改进后
目录结构如下:
初始化第三方库BYThirdPartService.m的代码如下:
#import "BYThirdPartService.h"@implementation BYThirdPartService+ (void)load{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //设置NUI配置 [self setNUIConfig]; //开启统计 [self startStatistics]; //键盘初始化 [self initKeyboard]; });}//设置NUI配置- (void)setNUIConfig{ //判断屏幕尺寸 CGFloat scale = [UIScreen mainScreen].scale; int scaleInt = (int)scale; NSString *nuiStyleStartName = @"BYDefault"; NSString *nuiStyleName = @"BYDefault.NUI"; [NUISettings initWithStylesheet:nuiStyleName]; if([NUISettings hasProperty:@"translucent" withClass:@"NavigationBar"]) { [[UINavigationBar appearance] setTranslucent:[NUISettings getBoolean:@"translucent" withClass:@"NavigationBar"]]; } if ([NUISettings hasProperty:@"tint-color" withClass:@"NavigationBar"]) { [[UINavigationBar appearance] setTintColor:[NUISettings getColor:@"tint-color" withClass:@"NavigationBar"]]; }}//键盘初始化- (void)initKeyboard{ IQKeyboardManager * manager = [IQKeyboardManager sharedManager]; manager.enable = YES; manager.shouldResignOnTouchOutside = YES; manager.shouldToolbarUsesTextFieldTintColor = YES; manager.enableAutoToolbar = YES;}//开始统计- (void)startStatistics{ [MobClick startWithConfigure:UMConfigInstance];}
初始化数据 BYInitData.m的代码(思路,具体代码根据自身项目的实际情况进行修改)
#import "BYInitData.h"@implementation BYInitData+ (void)load{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //初始化数据库 [self initDB]; //检测网络状态 [self GetServerStatus]; //获取用户信息 [self GetUserinfo]; });}//初始化数据库- (void)initDB{ [[BYDBHelper sharedInstance] startInitOrUpdate];}- (void)GetServerStatus{ //检测网络状态 ...........}- (void)GetServerStatus{ //获取用户信息 ...........}@end
简化后AppDelegate如下:
#import "AppDelegate.h"#import "BYCircleListViewController.h"#import "BYNavigationViewController.h"//只需增加相应的两个头文件#import "BYThirdPartService.h"#import "BYInitData.h"@implementation AppDelegate- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. BYCircleListViewController *homePageVC = [[BYCircleListViewController alloc] init]; BYNavigationViewController *navVC = [[BYNavigationViewController alloc] initWithRootViewController:homePageVC]; self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; self.window.rootViewController = navVC; [self.window makeKeyAndVisible]; return YES;}
当类被引入项目时, runtime 会向每一个类对象发送 load 消息. 神奇的load 方法, 会在每一个类甚至分类被引入时仅调用一次, 调用的顺序是父类优先于子类, 子类优先于分类. 而且 load 方法不会被类自动继承, 每一个类中的 load 方法都不需要像 viewDidLoad 方法一样调用父类的方法。
埋点统计
在iOS中,在运行时替换两个方法的实现,达到“勾住”某个方法并注入代码的目的。具体方法如下:
重载类的“+(void)load”方法,在程序加载到内存时利用Runtime的method_exchangeImplementations等接口将方法的实现互相交换。当方法M被调用时就会被勾住(Hook),执行我们的方法。
该技术称为Method Swizzling,属于面向切面编程(Aspect-Oriented Programming)的一种实现。
替换两个方法的实现,代码如下:
#import "BYStatistics.h"#import <objc/runtime.h>@implementation BYStatistics+ (void)swizzlingClass:(Class)cls originalSelector:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector{ Class class = cls; Method originalMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); //class_addMethod的返回BOOL代表的是isNotExist,即当前类未实现该方法时才能添加成功 BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (didAddMethod ) { class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); }else { method_exchangeImplementations(originalMethod, swizzledMethod); } }@end
BYStatistics统计类下文会用到。利用神奇的load方法统计两个页面的展示与离开次数
#import "UIViewController+Stastistics.h"#import "BYStatistics.h"@implementation UIViewController (Stastistics)+ (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ SEL originalSelector = @selector(viewWillAppear:); SEL swizzledSelector = @selector(swizzling_viewWillAppear:); [BYStatistics swizzlingClass:[self class] originalSelector:originalSelector swizzledSelector:swizzledSelector]; SEL originalSelector2 = @selector(viewWillDisappear:); SEL swizzledSelector2 = @selector(swizzling_viewWillDisappear:); [BYStatistics swizzlingClass:[self class] originalSelector:originalSelector2 swizzledSelector:swizzledSelector2]; });}#pragma mark - Method Swizzling- (void)swizzling_viewWillAppear:(BOOL)animated{ //插入需要执行的代码 [self inject_viewWillAppear]; [self swizzling_viewWillAppear:animated];}//利用hook,统计页面的停留时间- (void)inject_viewWillAppear{ NSString *pageName = [self pageEventName:YES]; if (pageName) { //统计代码 }}- (void)swizzling_viewWillDisappear:(BOOL)animated{ [self inject_viewWillDisappear]; [self swizzling_viewWillDisappear:animated];}- (void)inject_viewWillDisappear{ NSString *pageName = [self pageEventName:YES]; if (pageName) { //统计代码 }}@end
load方法与initialize方法
NSObject的load方法和initialize方法都是用来实现初始化操作。
load方法
对于加入运行期系统中的每个类及分类来说,必定会调用此方法,而且近调用一次。当包含类或分类的程序库载入系统时,就会执行此方法,而这通常就是指应用程序启动的时候,若程序是为iOS平台设计的,则肯定会在此时执行。
如果分类和其所属的类都定义了load方法,则先调用类里的,在调用分类里的。在执行子类的load方法之前,必定会先执行所有超类的load方法,而如果代码还依赖了其他程序库,那么程序库里相关类的load方法也必定会先执行。
在整个应用程序执行load方法时都会阻塞initialize方法
它是“惰性”调用的,也就是说,只有当程序用到了相关的类时,才会调用。因此,如果某个类一直都没有使用,那么其initialize方法就一直不会运行。这也就等于说,应用程序无须先把每个类的initialize都执行一遍
注意事项
- 与其他方法不同,load方法不参与覆写机制
- load方法实现得精简一些,有助于保持应用程序的响应能力,也能减少引入”依赖环”的几率。
如有写的不对地方,请在评论区指出,谢谢!
请指明出处:
https://jingwanli6666.github.io/2016/11/08/%E7%A5%9E%E5%A5%87%E7%9A%84load%E6%96%B9%E6%B3%95/
- 神奇的load方法
- jquery的load方法----$().load
- Python的神奇方法指南
- python的神奇方法指南
- Python 的神奇方法指南
- Python 的神奇方法指南
- Python的神奇方法指南
- scala神奇的sortBy方法
- JQuery 的load()方法
- jQuery的 Load 方法
- NSObject 的 load 方法
- JQUERY的load方法
- jQuery的load()方法
- jquery的load方法
- ajax 的load方法
- 平凡而又神奇的贝叶斯方法
- 平凡而又神奇的贝叶斯方法
- 平凡而又神奇的贝叶斯方法
- ffmpeg在window 7下的编译过程,整合效率高
- 【专题】树状数组
- ArrayList、LinkedList、Vector、Stack的区别
- Ubuntu SSE指令集 编程实例---复数乘法与共轭乘法
- asp.net——MVC点击图片跳转Action
- 神奇的load方法
- 219. Contains Duplicate II
- 临界区,互斥量,信号量,事件的区别
- ubuntu16.04下安装ros教程
- ffmpeg解码音频保存为PCM
- 上传组件FileUpload组件
- Java内部类笔记9(备忘)
- printf和scanf
- 文章标题