http://www.cocoachina.com/ios/20151117/14167.html

简介

在iOS工程中,AppDelegate往往会有上千行,甚至几千行,这样就会给维护AppDelegate带来诸多麻烦。比方说,老板想在出现HomeViewController之前弹出广告并停顿几秒,这样你就要加入插入广告的逻辑;又比方说,老板想在开始做个请求,判断某个开关是否打开。这样就会在AppDelegate中插入很多相关的不相关的代码。

在AppDelegate中,- (BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions是“Tells the delegate when the application has launched and may have additional launch options to handle.”,即在app开始运行时会调用里面的方法。在didFinishLaunchingWithOptions中,我们往往会渲染window,注册第三方监控库,加入基本页面跳转逻辑。

下面是一个常见项目中的didFinishLaunchingWithOptions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// objective-c语言
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
         
    if (!([ADeanUserDataManager sharedManager].userName != nil &&
          [ADeanUserDataManager sharedManager].userName.length > 0 &&
          [ADeanUserDataManager sharedManager].userPassword != nil &&
          [ADeanUserDataManager sharedManager].userPassword.length > 0)) {
         
        // 用户名、密码为空时候强制为未登录
        [ADeanUserDataManager sharedManager].isUserLogined = @NO;
    }
     
    self.window.rootViewController = self.tabbarController;
    [self.window makeKeyAndVisible];
     
    //  基本页面跳转逻辑
    /*--------------------------------------*/
    if ([[ADeanUserDataManager sharedManager].everLaunched boolValue] == NO) {
    //是否是第一次启动判断
        [ADeanUserDataManager sharedManager].everLaunched = [NSNumber numberWithBool:YES];
        [self.window addSubview:self.helpViewController.view];
    }
    /*--------------------------------------*/
     
    //  注册第三方库 
    /*--------------------------------------*/
    // 注册Crash统计 -- Crashlytics
    [Fabric with:@[[Crashlytics class]]];
    [MobClick startWithAppkey:UMENG_APPKEY];
    [MobClick setCrashReportEnabled:NO]; // 关掉MobClick Crash Report收集开关
#ifdef ADeanForTest
    [MobClick setCrashReportEnabled:YES]; // 打开MobClick Crash Report收集开关
    [MobClick setLogEnabled:YES];
#endif
     
    [ShareSDK registerApp:ShareSDKAppKey];
     
    //新浪
    [ShareSDK connectSinaWeiboWithAppKey:SinaAppKey
                               appSecret:SinaAppSecret
                             redirectUri:SinaRedirectUri];
     
    //新浪微博客户端应用
    [ShareSDK connectSinaWeiboWithAppKey:SinaAppKey
                               appSecret:SinaAppSecret
                             redirectUri:SinaRedirectUri
                             weiboSDKCls:[WeiboSDK class]];
     
#if TARGET_IPHONE_SIMULATOR
#else
    //QQ好友
    [ShareSDK connectQQWithQZoneAppKey:QZoneAppKey
                     qqApiInterfaceCls:[QQApiInterface class]
                       tencentOAuthCls:[TencentOAuth class]];
#endif
    //微信朋友圈
    [ShareSDK connectWeChatSessionWithAppId:WeiXinAppID wechatCls:[WXApi class]];
    //微信好友
    [ShareSDK connectWeChatTimelineWithAppId:WeiXinAppID wechatCls:[WXApi class]];
    [MiPushSDK registerMiPush:self type:(UIRemoteNotificationTypeBadge |
                                         UIRemoteNotificationTypeSound |
                                         UIRemoteNotificationTypeAlert) connect:YES];
    /*--------------------------------------*/
                                          
    //  其他逻辑
    [self registerRemotePushNotification];
    [self getSwitchInfoFromService];
    [self appIntegrityCheck];
    [self appSecurityCheck]
    ......
     
    return YES;
}

下面我们就来看看,有什么好的办法可以对AppDelegate进行瘦身,加强代码的可读性和可维护性,并将代码放到适当的地方。

函数模块化

上述didFinishLaunchingWithOptions中可以按照功能逻辑划分为:处理启动逻辑,注册第三方库,处理其他逻辑三类。这样就可以优化为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// objective-c语言
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
         
    if (!([ADeanUserDataManager sharedManager].userName != nil &&
          [ADeanUserDataManager sharedManager].userName.length > 0 &&
          [ADeanUserDataManager sharedManager].userPassword != nil &&
          [ADeanUserDataManager sharedManager].userPassword.length > 0)) {
         
        // 用户名、密码为空时候强制为未登录
        [ADeanUserDataManager sharedManager].isUserLogined = @NO;
    }
     
    self.window.rootViewController = self.tabbarController;
    [self.window makeKeyAndVisible];
     
    //  基本页面跳转逻辑
    [self baseViewJumpLogic];
    //  注册第三方库 
    [self registThirdPart];                     
    //  其他逻辑
    [self handleOtherLogic]
     
    return YES;
}
- (void)baseViewJumpLogic {
    if ([[ADeanUserDataManager sharedManager].everLaunched boolValue] == NO) {
    //是否是第一次启动判断
        [ADeanUserDataManager sharedManager].everLaunched = [NSNumber numberWithBool:YES];
        [self.window addSubview:self.helpViewController.view];
    }
}
- (void)registThirdPart {
    // 注册Crash统计 -- Crashlytics
    [Fabric with:@[[Crashlytics class]]];
    [MobClick startWithAppkey:UMENG_APPKEY];
    [MobClick setCrashReportEnabled:NO]; // 关掉MobClick Crash Report收集开关
#ifdef ADeanForTest
    [MobClick setCrashReportEnabled:YES]; // 打开MobClick Crash Report收集开关
    [MobClick setLogEnabled:YES];
#endif
     
    [ShareSDK registerApp:ShareSDKAppKey];
     
    //新浪
    [ShareSDK connectSinaWeiboWithAppKey:SinaAppKey
                               appSecret:SinaAppSecret
                             redirectUri:SinaRedirectUri];
     
    //新浪微博客户端应用
    [ShareSDK connectSinaWeiboWithAppKey:SinaAppKey
                               appSecret:SinaAppSecret
                             redirectUri:SinaRedirectUri
                             weiboSDKCls:[WeiboSDK class]];
     
#if TARGET_IPHONE_SIMULATOR
#else
    //QQ好友
    [ShareSDK connectQQWithQZoneAppKey:QZoneAppKey
                     qqApiInterfaceCls:[QQApiInterface class]
                       tencentOAuthCls:[TencentOAuth class]];
#endif
    //微信朋友圈
    [ShareSDK connectWeChatSessionWithAppId:WeiXinAppID wechatCls:[WXApi class]];
    //微信好友
    [ShareSDK connectWeChatTimelineWithAppId:WeiXinAppID wechatCls:[WXApi class]];
    [MiPushSDK registerMiPush:self type:(UIRemoteNotificationTypeBadge |
                                         UIRemoteNotificationTypeSound |
                                         UIRemoteNotificationTypeAlert) connect:YES];
}
- (void)handleOtherLogic {
    [self registerRemotePushNotification];
    [self getSwitchInfoFromService];
    [self appIntegrityCheck];
    [self appSecurityCheck]
    ......
}

模块化后,代码瞬间变得易读很多,而且需要改什么可以直接去相应的模块添加。但是这个仅仅是将代码的顺序变化下,相同功能的代码抽到一个函数中,代码行数没有减少,所有的功能还是糅合在一个.m中。

类模块化

很多其他逻辑是业务逻辑的,可以抽离到业务Model中,通过类模块化便捷使用。这样就可以优化为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// objective-c语言
#import "ADeanAppCheck.h"
#import "ADeanSwitches.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
         
    if (!([ADeanUserDataManager sharedManager].userName != nil &&
          [ADeanUserDataManager sharedManager].userName.length > 0 &&
          [ADeanUserDataManager sharedManager].userPassword != nil &&
          [ADeanUserDataManager sharedManager].userPassword.length > 0)) {
         
        // 用户名、密码为空时候强制为未登录
        [ADeanUserDataManager sharedManager].isUserLogined = @NO;
    }
     
    self.window.rootViewController = self.tabbarController;
    [self.window makeKeyAndVisible];
     
    //  基本页面跳转逻辑
    [self baseViewJumpLogic];
    //  注册第三方库 
    [self registThirdPart];                     
    //  其他逻辑
    [self handleOtherLogic]
     
    return YES;
}
- (void)handleOtherLogic {
    [ADeanAppCheck appInfoCheck]; // Integrity & Security Check
    [ADeanSwitches appSwitchInit];  // Get Switch From Service 
    ......
}

分类模块化

先抛个问题:分类中是否可以定义变量?

如果不知道可以参考:iOS分类中通过runtime添加动态属性

分类能够做到的事情主要是:即使在你不知道一个类的源码情况下,向这个类添加扩展的方法。这里我们主要是将对外开放的方法和一部分变量拿到分类中处理。这样进一步轻量化AppDelegate本身进行代码量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// objective-c语言
// ADeanAppDelegate+Light.h文件
#import "AppDelegate.h"
@interface ADeanAppDelegate (Light)
@property (nonatomic, strong) UITabbarController *tabbarController;
/*!
 @brief 全局appDeleaget
 */
+ (AppDelegate *)appDelegate;
/*!
 @method
 @brief 关闭系统键盘
 */
+ (void)closeKeyWindow;
@end
// objective-c语言
// ADeanAppDelegate+Light.m文件
#import "ADeanAppDelegate+Light.h"
- (UITabbarController *)tabbarController {
    UITabbarController *tabbarController = objc_getAssociatedObject(self, &kTabbarControllerObjectKey);
    if (!tabbarController) {
        tabbarController = [[UITabbarController alloc] init];
        objc_setAssociatedObject(self, &kTabbarControllerObjectKey, tabbarController, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return tabbarController;
}
- (void)setTabbarController:(UITabbarController *)tabbarController {
    objc_setAssociatedObject(self, &kTabbarControllerObjectKey, tabbarController, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
+ (AppDelegate *)appDelegate {
    return (AppDelegate *)[[UIApplication sharedApplication] delegate];
}
+ (void)closeKeyWindow {
    [[UIApplication sharedApplication].keyWindow endEditing:YES];
}
这样在AppDelegate中,对外开放的方法和部分变量可以抽离到分类中去。也可以根据作用定义不同的AppDelegate分类:
#“ADeanAppDelegate+View.h”
#“ADeanAppDelegate+Controller.h”
#“ADeanAppDelegate+Method.h”

这样代码结构会更加清晰明了。 抽出来的AppDelegate只剩下注册第三方库了,因为第三方库很多是需要在didFinishLaunchingWithOptions中运行,正常的方法就很难。

Method Swizzling化

Method Swizzling是改变一个selector的实际实现的技术,关于Method Swizzling的概念、原理谷歌一堆。

Method Swizzling中以viewWillAppear为例,讲解了Method Swizzling的基本用法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#import "ADeanAppDelegate+Hook.h"
#import "ADeanMethodSwizzling.h"
#import "MobClick.h"
#import "WXApi.h"
#import "WeiboSDK.h"
#import 
#import 
#import 
#if TARGET_IPHONE_SIMULATOR
#else
#import 
#import 
#import 
#endif
@implementation ADeanAppDelegate (Hook)
+ (void)initialize
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self adean_AppDelegateHook];
    });
}
+ (void)adean_AppDelegateHook
{
    SwizzlingMethod([ADeanAppDelegate class], @selector(application:didFinishLaunchingWithOptions:), @selector(adean_application:didFinishLaunchingWithOptions:));
    SwizzlingMethod([ADeanAppDelegate class], @selector(application:handleOpenURL:), @selector(adean_application:handleOpenURL:));
    SwizzlingMethod([ADeanAppDelegate class], @selector(application:openURL:sourceApplication:annotation:), @selector(adean_application:openURL:sourceApplication:annotation:));
    SwizzlingMethod([ADeanAppDelegate class], @selector(applicationDidReceiveMemoryWarning:), @selector(adean_applicationDidReceiveMemoryWarning:));
}
#pragma mark - Method Swizzling
- (BOOL)adean_application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
     
     
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 耗时的操作
        // 注册Crash统计 -- Crashlytics
        [Fabric with:@[[Crashlytics class]]];
         
        // 友盟统计
        [MobClick startWithAppkey:UMENG_APPKEY];
        [MobClick setCrashReportEnabled:NO]; // 关掉MobClick Crash Report收集开关
#ifdef ADeanForTest
        [MobClick setCrashReportEnabled:YES]; // 打开MobClick Crash Report收集开关
        [MobClick setLogEnabled:YES];
#endif
         
         
        [ShareSDK registerApp:ShareSDKAppKey];
         
        //新浪
        [ShareSDK connectSinaWeiboWithAppKey:SinaAppKey
                                   appSecret:SinaAppSecret
                                 redirectUri:SinaRedirectUri];
         
        //新浪微博客户端应用
        [ShareSDK connectSinaWeiboWithAppKey:SinaAppKey
                                   appSecret:SinaAppSecret
                                 redirectUri:SinaRedirectUri
                                 weiboSDKCls:[WeiboSDK class]];
         
#if TARGET_IPHONE_SIMULATOR
#else
        //QQ好友
        [ShareSDK connectQQWithQZoneAppKey:QZoneAppKey
                         qqApiInterfaceCls:[QQApiInterface class]
                           tencentOAuthCls:[TencentOAuth class]];
#endif
        //微信朋友圈
        [ShareSDK connectWeChatSessionWithAppId:WeiXinAppID wechatCls:[WXApi class]];
        //微信好友
        [ShareSDK connectWeChatTimelineWithAppId:WeiXinAppID wechatCls:[WXApi class]];
    });
    return [self adean_application:application didFinishLaunchingWithOptions:launchOptions];
}
- (BOOL)adean_application:(UIApplication *)application handleOpenURL:(NSURL *)url
{
    [ShareSDK handleOpenURL:url wxDelegate:self];
    return [self adean_application:application handleOpenURL:url];
}
- (BOOL)adean_application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
    [ShareSDK handleOpenURL:url sourceApplication:sourceApplication annotation:annotation wxDelegate:self];
    return [self adean_application:application openURL:url sourceApplication:sourceApplication annotation:annotation];
}
-  (void)adean_applicationDidReceiveMemoryWarning:(UIApplication *)application {
     
    [self adean_applicationDidReceiveMemoryWarning:application];
}
@end

这下再去看下AppDelegate文件,代码不超过200行了。

小结

Method Swizzling常见的应用场景:

1.用于记录或者存储,比方说记录ViewController进入次数、Btn的点击事件、ViewController的停留时间等等。 可以通过Runtime获取到具体ViewController、Btn信息,然后传给服务器。

2.添加需要而系统没提供的方法,比方说修改Statusbar颜色。

3.用于轻量化、模块化处理,如上面介绍的,代码轻量化处理。

Method Swizzling是把双刃剑,需要正确理解它的使用。

分类增加变量的使用场景:

1.过多继承时,可以通过分类减少继承层级,清晰流程框架。比方说,ViewController可能需要相互冲突的事件,单一父类会导致逻辑复杂。这时候可以通过分类简化逻辑,不同的ViewController引用不同的分类。

2.扩展类属性。

上面我们学习了一些瘦身的技巧,希望通过这些方法写出更可读性更高,可维护性更高的代码。

提醒:

本文涉及到的Demo已经放到GitHub上了。Demo可能与本文有点出入,部分函数命名跟文章中不一致。

iOS Method Swizzling和分类的妙用AppDelegate轻量化处理的更多相关文章

  1. ios method swizzling

      阅读器 iOS开发iOS   本文由TracyYih[博客]翻译自NSHipster的文章Method Swizzling.   在上周associated objects一文中,我们开始探索Ob ...

  2. IOS 开发之 Method Swizzling + Category

    ios 分类中如果增加的方法与被扩展的类方法名重复,则原方法就没法被调用….看以下例子 例如: @interface ClassA : NSObject - (NSString *) myMethod ...

  3. ios逆向工程-内部钩子(Method Swizzling)

    Method Swizzling(方法调配) 怎么说呢,先了解什么是钩子为什么用钩子,学过C++的朋友应该清楚,hook就是用来获得(截断/改变)底层调用的方法.这样我们可以自由的修改或者读取一些想要 ...

  4. IOS 开发之 Method Swizzling

    ios 分类中如果增加的方法与被扩展的类方法名重复,则原方法就没法被调用….看以下例子 例如: @interface ClassA : NSObject - (NSString *) myMethod ...

  5. 【原】iOS动态性(三) Method Swizzling以及AOP编程:在运行时进行代码注入

    概述 今天我们主要讨论iOS runtime中的一种黑色技术,称为Method Swizzling.字面上理解Method Swizzling可能比较晦涩难懂,毕竟不是中文,不过你可以理解为“移花接木 ...

  6. iOS中AOP与Method Swizzling 项目中的应用

    引子:项目中需要对按钮点击事件进行统计分析,现在项目中就是在按钮的响应代码中添加点击事件,非常繁琐.所以使用了AOP(面向切面编程),将统计的业务逻辑统一抽离出来. 项目中添加的开源库:https:/ ...

  7. iOS运行时与method swizzling

    C语言是静态语言,它的工作方式是通过函数调用,这样在编译时我们就已经确定程序如何运行的.而Objective-C是动态语言,它并非通过调用类的方 法来执行功能,而是给对象发送消息,对象在接收到消息之后 ...

  8. iOS 使用Method Swizzling隐藏Status Bar

    在iOS 6中,隐藏Status Bar很的简单. // iOS 6及曾经,隐藏状态栏 [[UIApplication sharedApplication] setStatusBarHidden:YE ...

  9. iOS执行时与method swizzling

    C语言是静态语言,它的工作方式是通过函数调用,这样在编译时我们就已经确定程序怎样执行的.而Objective-C是动态语言,它并不是通过调用类的方法来执行功能,而是给对象发送消息,对象在接收到消息之后 ...

随机推荐

  1. OSGi.NET使用笔记

    一手资料来源于“开放工厂”,以下程序将会引用到一个核心文件UIShell.OSGi.dll 目前我对于OSGi这个框架的理解就是,主程序搜索并加载插件,以插件方式开放,便于扩展. 现在开始正式的旅程. ...

  2. Maven实战02_Maven的安装和配置

    1:在Windows上安装Maven 本人系统环境:win10  + JDK1.8 + apache-maven-3.3.9 在安装Maven之前,首先要确定你的Java环境是否已经配置好了,你是否已 ...

  3. c#日期时间截取

    时间格式化CodeDateTime dt = DateTime.Now;Label11.Text = dt.ToString();2005-11-5 13:21:25Label12.Text = dt ...

  4. HTML5:使用Canvas和Input range控件放大缩小图片,剪裁,并上传图片

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  5. tensorflow根据pb多bitch size去推导物体

    with self.detection_graph.as_default(): with tf.Session(graph=self.detection_graph) as sess: # Expan ...

  6. Redis源码解析:21sentinel(二)定期发送消息、检测主观下线

    六:定时发送消息 哨兵每隔一段时间,会向其所监控的所有实例发送一些命令,用于获取这些实例的状态.这些命令包括:"PING"."INFO"和"PUBLI ...

  7. Eclipse:Eclipse插件开发全套教程

    分享是美德,作者为Eclipse核心工程师之一,全英文版,有不明白的地方欢迎探讨和咨询. http://www.vogella.com/tutorials/eclipse.html

  8. 如何获取Expression Design 4工具与Expression Blend 4工具

    在VS2010+C#+WPF 开发项目过程中涉及到界面的布局与设计,网上有人讲采用Expression Design 4与Expression Blend 4工具相当方便, 于是决定试看看,下面将这个 ...

  9. Eclipse luna安装SVN

    Eclipse luna安装SVN 1.Subversive Plug in 的安装 打开Eclipse ,Help--->Install New Soft ----> 输入 “Luna ...

  10. [翻译] MaxMind DB 文件格式规范

    MaxMind DB 文件格式规范来源:http://maxmind.github.io/MaxMind-DB/翻译:御风(TX:965551582)2017-03-23 -------------- ...