Giter VIP home page Giter VIP logo

delegatedietdemo's Introduction

本文原地址:http://kyson.cn/index.php/archives/105/

AppDelegate瘦身是一个大家都很熟悉的话题,各家也有各自的解决方案。但方案无外乎两种,一种是从AppDelegate本身入手,通过各种方式减少AppDelegate的代码行数,另一种是通过架构层面就解决了。本文将分别介绍这两种方式的代表性库,并对比其优缺点。

FRDModuleManager

FRDModuleManager是豆瓣开源的轻量级模块管理工具。它通过减小AppDelegate的代码量来把很多职责拆分至各个模块中去,这样 AppDelegate 会变得容易维护。其主要类FRDModuleManager只有300多行代码,使用方式也很简单:

  1. 加载模块
NSString* plistPath = [[NSBundle mainBundle] pathForResource:@"ModulesRegister" ofType:@"plist"];
FRDModuleManager *manager = [FRDModuleManager sharedInstance];
[manager loadModulesWithPlistFile:plistPath];
  1. 在 UIApplicationDelegate 各方法中留下钩子
NSString* plistPath = [[NSBundle mainBundle] pathForResource:@"ModulesRegister" ofType:@"plist"];
FRDModuleManager *manager = [FRDModuleManager sharedInstance];
[manager loadModulesWithPlistFile:plistPath];
//...这里省略其他的多个生命周期方法

实现原理: FRDModuleManager的实现很简单,其内部数组持有注册的模块的引用,通过依次调用数组中的每个模块的特定方法来达到解耦的目的:

FRDModuleManager原理图

优点:

  • 简单,只需要几行代码就可以解决。
  • 被添加的每个模块都可以“享受”AppDelegate的各个生命周期。

缺点:

  • 每个模块都要初始化并分配内存,当FRDModuleManager里注册了大量模块时,会创建大量对象并影响App启动速度。
  • 缺少模块初始化优先级,当有三个模块A,B,C时,正好C依赖于B,B依赖于A,如果在配置文件中配置A,B,C的顺序又是打乱时,初始化会出问题。

JSDecoupledAppDelegate

JSDecoupledAppDelegate是由JSBadgeView的作者开发的一款轻量级的AppDelegate解耦工具,笔者的个人项目壁纸宝贝正在使用这个库。它将AppDelegate各个功能点独立出来,并通过代理的方式将控制权下发。我们可以看到JSDecoupledAppDelegate类中有很多代理,这边列举几个:

代理名 协议 描述
appStateDelegate JSApplicationStateDelegate App各种状态
appDefaultOrientationDelegate JSApplicationDefaultOrientationDelegate App的横竖屏切换
remoteNotificationsDelegate JSApplicationRemoteNotificationsDelegate App通知代理

这些代理见名知意,例如appStateDelegate是用于处理App的各种状态(didFinishLaunchingWithOptions、applicationDidBecomeActive等)下的逻辑;remoteNotificationsDelegate是用于处理App的推送的逻辑。JSDecoupledAppDelegate使用起来也非常简单:

  1. main.m中的AppDelegate替换成JSDecoupledAppDelegate
return UIApplicationMain(argc, argv, nil, NSStringFromClass([JSDecoupledAppDelegate class]));
  1. 指定你需要处理的各个JSDecoupledAppDelegate的子delegate。例如,你需要实现 didFinishLaunchingWithOptions方法,则新建一个类,在其中添加
+ (void)load
{
    [JSDecoupledAppDelegate sharedAppDelegate].appStateDelegate = [[self alloc] init];
}

然后就可以在里面实现我们以前在didFinishLaunchingWithOptions的方法。

实现原理: iOS开发的小伙伴应该对Objective-C的消息转发机制有所了解,JSDecoupledAppDelegate就是通过转发AppDelegate的各个方法来实现AppDelegate的解耦的:

JSDecoupledAppDelegate调用流程

优点:

  • JSDecoupledAppDelegate本身对于AppDelegate各个功能的拆分对我们理解AppDelegate有一定帮助——AppDelegate确实承载了太多的功能。
  • 由于各个子代理对象的执行顺序是确定的,因此基本可以解决FRDModuleManager中相互依赖的问题。

缺点: JSDecoupledAppDelegate的缺点非常明显:使用它必须废弃原生的AppDelegate,因此我们不能通过

((AppDelegate *)[UIApplication sharedApplication].delegate).window

来获取window,以及windowrootViewController。这个问题笔者曾经提issue给作者,作者的回答也是只能通过获取view.window等曲线救国的方式获取window

AppDelegate分类(Category)

创建AppDelegate分类无疑是低投入高产出的最好解决方案了。目前笔者公司的项目正在使用该方式。不需要添加任何三方库,我们就可以给AppDelegate添加很多方法,并且能轻松控制方法的执行顺序,通过此方式,我们项目的AppDelegate.m文件文件大小成功瘦身至200行+:

新建分类文件

在AppDelegate中调用

然而分类的缺点也不言而喻:添加新的属性比较繁琐,只能通过runtime或者BlocksKit等三方库实现。

以上三种方法都是通过对AppDelegate修改或添加的方式来达到降低耦合的,下面介绍几种从App架构层就降低AppDelegate耦合性的解决方案。

JLRoutes

JLRoutesgithub上Star数目比较多的URL解析库,可以很方便的处理不同URL Scheme以及解析它们的参数,并通过回调block来处理URL对应的操作。我们可以通过定义URL的规则来定制我们的页面跳转或其他逻辑。例如假设我们需要在执行ServiceMediator类中的start方法,只需要

  1. 定义URL,这里我们设置为
NSString *customURL = @"JLRoutesDemo://Service/ServiceMediator";

JLRoutesDemo是我们项目的URL Scheme,需要在plist中添加。

  1. 对我们注册的URL进行处理:
// 在route表中添加一条记录
[JLRoutes addRoute:@"/Service/:servicename" handler:^BOOL(NSDictionary<NSString *,NSString *> * _Nonnull parameters) {
    // 处理函数
    Class className = NSClassFromString(parameters[@"servicename"]);
    [className performSelector:@selector(startRoute)];
    return YES;
}];

具体代码大家可以看笔者的Demo

实现原理: JLRoutes在内部维护了一份URL字典,注册时添加元素,移除时删除元素。

MGJRouter

MGJRouter是一个高效/灵活的iOS URL Router,解决了JLRoutes查找URL不够高效,通过遍历而不是匹配的问题。这里不多做介绍了,大家可以自行Google。

Objection

Objection是一个轻量级的依赖注入框架。依赖注入对于客户端开发的我们可能不太熟悉,但服务端中使用很多,比如Java的Spring框架和PHP的laravel框架。 依赖注入的核心**就是控制权反转(Inversion of Control,IoC)。传统iOS程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建。具体到Objective-C中就是,先定义一个协议(protocol),然后通过objection来注册这个协议对应的class,需要的时候,可以获取该协议对应的object。对于使用方无需关心到底使用的是哪个Class,反正该有的方法、属性都有了(在协议中指定):

// 先在App启动之前初始化容器
+(void)load
{
    JSObjectionInjector *injector = [JSObjection defaultInjector];
    injector = injector ? : [JSObjection createInjector];
    injector = [injector withModule:[[xxxModule alloc] init]];
    [JSObjection setDefaultInjector:injector];
}

xxxModule就是我们需要绑定绑定protocol和具体实现类的地方,假设我们有两个服务需要启动,可以如下处理:

//xxxModule.m文件
[self bindClass:[NotificationService class] toProtocol:@protocol(NotificationServiceProtocol)];
[self bindClass:[ShareService class] toProtocol:@protocol(ShareServiceProtocol)];

接着我们只要通过如下代码获取这两个对象:

// 通知服务
JSObjectionInjector *injector = [JSObjection defaultInjector];
NSObject<NotificationServiceProtocol> *notificationService = [injector getObject:@protocol(NotificationServiceProtocol)];
// 分享服务
NSObject<ShareServiceProtocol> *shareService = [injector getObject:@protocol(ShareServiceProtocol)];

这样一来notificationServiceshareService就被创建了,我们可以在这两个对象中编写我们的逻辑,省去了在AppDelegate中编写相应的代码,从而降低了耦合性。如果大家对这个库还有疑问,可以参考笔者的Demo。

总结

本文主要讲解了通过两种方式来瘦身AppDelegate,虽然有所区别,但大致思路还是差不多的。希望对大家有所帮助。本文Demo的地址:https://github.com/zjh171/DelegateDietDemo

参考

豆瓣App的模块化实践

AppDelegate解耦之JSDecoupledAppDelegate

【源码阅读】JLRoutes

蘑菇街 App 的组件化之路

dependency injection 关于IOS依赖注入那些事

使用objection来模块化开发iOS项目

delegatedietdemo's People

Contributors

kysonzhu avatar zjh171 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

delegatedietdemo's Issues

AppDelegate分类(Category)问题

AppDelegate分类(Category)方法,后期是不是会在delegate中调用很多分类方法,
或者是每个分类交换方法,感觉这两种方式都有点问题吧?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.