关于ZAKER 融媒体解决方案 合作 加入

iOS 使用“注解”实现“微服务”路由

CocoaChina 11-22

前言

大家知道 Objective-C 本身是没有支持注解功能的,但有时使用注解将大幅提高效率,同时让代码更简单易懂。特别是今天要介绍的一个关于 " 微服务 " 注册的场景。

什么是 " 微服务 "

微服务是目前后端提的比较多的一个东西,从广义上来说就是一个去中心化的开发模式,通过各个组件的自注册,达到服务分发的效果。

那么跟客户端有什么关系呢?有一个很具体的例子就是 " 界面路由 "。这也是近期大家谈的比较多的一个事(包括我最近也在做),相信很多同学并不陌生。具体的做法就是,我们会对各个界面定义一个 ID,比如首页 -main、详情页 -detail、关于页 -about 等等,然后通过路由管理器来分发,从而展示指定的 Controller。

路由方案一

一个比较简单的做法是建立 ID 到 Controller 的映射表,然后根据 Controller 类名创建对象,进行 push 操作:

NSString *controllerClazz = [ routeConfig clazzForID:@"main" ] ;Class controllerClass = NSClassFromString ( controllerClazz ) ;// check the controller class...UIViewController *controller = [ [ controllerClass alloc ] init ] ;// initialize parameters... [ topVC pushViewController:controller animated:YES ] ;

这种做法的好处是足够简单,但是规则太过于死板,无法根据业务做定制。

一种改进的方案是做一个分发器,建立 ID 到展示界面方法的映射。

路由方案二

@implement RouterDispatcher- ( void ) dispatchMain { // 定制 [ topVC pushViewController:mainViewController animated:YES ] ;}- ( void ) dispatchDetail { // 定制 [ topVC pushViewController:detailViewController animated:YES ] ;}@end

这个方案增加了一层中转,方便业务的定制,不过缺点也是明显的,大量的代码都堆砌在了 RouterDispatcher 类里。这意味着每次新增界面或者修改业务都需要改动到这个类,显然作为一个底层核心库,我们希望最大限度地剥离业务以避免改动与保障稳定性。如何去掉这个中心化,就利用到了我们前文所提的 " 微服务 " 思想。

微服务路由

作为底层框架,我们不想关心一个 ID 具体是如何被路由的,我们只提供这种分发能力,具体的业务通过上层注册来实现。以下面的代码为例:

@implement RouterDispatcherstatic NSMutableDictionary<NSString */*page*/, NSString */*dispatcherClazz*/> kPages;+ ( void ) registerPage: ( NSString * ) pageID { if ( kPages == nil ) { kPages = [ [ NSMutableDictionary alloc ] init ] ; } kPages [ page ] = NSStringFromClass ( [ self class ] ) ;}+ ( void ) dispatchPage: ( NSString * ) pageID { NSString *dispatcherClazz = kPages [ pageID ] ; if ( dispatcherClazz == nil ) { return; } Class dispatcherClass = NSClassFromString ( dispatcherClazz ) ; RouterDispatcher dispatcher = [ [ dispatcherClass alloc ] init ] ; [ dispatcher dispatch ] ;}@end

我们只提供了 registerPage 注册方法以及 dispatchPage 分发方法。dispatchPage 方法的实现很简单:根据已注册的分发器做转发。

那么如何以 " 微服务 " 的形式做注册呢?

我们注意到了 NSObject 的 load 方法,这个方法会在类被加载的时候执行,显然用来做服务注册是再合适不过了——类被加载了意味着这个类可用,与此同时注册上服务意味着服务也是可用的。这也符合 " 微服务 " 启动自注册的理念。

终上,要完成对 "main" 的路由,只需要以下 3 个步骤:

继承 RouterDispatcher 实现 MainRouterDispatcher

使用 load 来注册 "main" 服务

实现 dispatch 完成路由分发

@implement MainRouterDispatcher+ ( void ) load { [ self registerPage:@"main" ] ;}- ( void ) dispatch { // 业务逻辑 [ topVC pushViewController:mainViewController animated:YES ] ;}@end

好了,现在底层框架基本 OK 了,但是对于一线开发来说,重复的写 + ( void ) load 显然是件很啰嗦的事,而且看上去不够醒目,容易被忽略。那么这个时候就是 " 注解 " 一展身手的时候了。

使用注解

最终的效果是:

@page ( @"main" ) - ( void ) dispatch { // Do stuff...}

首先,注解的基本格式为 @annotation ( attr ) ,官方常见的一些 @ 打头的关键字包括:

@property

@synthesize

@dynamic

@interface

@implement

我们可以使用宏来做替换,比如定义了:

#define my_property property

就可以使用 @my_property 来替代 @property,达到一样的效果。

但是这里有一个前提,我们选用的 @xx 需要在 @implement @end 区间内部来使用,比较符合的是 @synthesize 、@dynamic,但缺点是其后面必须带上一个属性,比如 @synthesize title; ,如果当前类没有属性就无法定义。

这个时候,我们注意到了一个不常用的 @compatibility_alias。这个注解是用于类名兼容的,一般开发不会用到。不过在框架开发中可能派上用场。

讲到这里,顺便提下我在开发 Pbind 过程中的一个小插曲。当时 Pbind 内部实现了一个类 PBRequest 用来统一封装 API 请求,突然有一天发现 Apple 的私有库 ProtocolBuffer.framework 也有这么一个同名的类,控制台输出警告:" 类名冲突,系统会选择其中一个而忽略另一个 "。这就尴尬了,谁知道你哪天选哪个呢?保险起见,只能自己换掉,初步的想法是用 _ PBRequest 替换 PBRequest,但是回头想想这个类是面向开发者的,我一大波的 PB 打头类,突然碰上一个 _ PB 打头的不是很奇怪么?偶然间发现了 @compatibility_alias 神器,两步即可搞定:

修改 PBRequest 为 _PBRequest ( .h 跟 .m 文件 )

在修改后的 _PBRequest.h 文件中加上一句:

@compatibility_alias PBRequest _PBRequest;

于是,其他的 所有 引用到 PBRequest 的地方都不需要改动,甚至接入这个库的使用者依然可以直接使用 PBRequest,因为大家都处于同一个编译环境下。

OK,回到我们的话题上来,我们来使用这个神奇的 @compatibility_alias 完成我们的 " 注解 ":

#define page ( _pageID_ ) ncompatibility_alias _RouterDispatcher RouterDispatcher; n+ ( void ) load { n [ self registerPage:_pageID_ ] ; n}

通过上述定义,使用 @page ( @"main" ) 时将会被展开:

@compatibility_alias _RouterDispatcher RouterDispatcher; + ( void ) load { [ self registerPage:@"main" ] ; }

相比我们最初给出的代码,这里唯一添加的一句 " 废话 " 就是:@compatibility_alias _RouterDispatcher RouterDispatcher; 即允许你在当前环境下使用 _"RouterDispatcher" 类,显然你不会用到它。不过我们也不需要过多地关注它,这句代码是在编译时做的,并不会影响到运行时。

OK,现在我们可以很方便地使用这个注解在任何地方,完成各个界面的路由。更重要的是 load 方法是系统加载类时自动触发的,这意味着你可以把分发器实现在各个地方,包括你所实施的组件化的某一个 Library 或者 Framework 里。

总结

本文介绍了 " 微服务 " 的基本思想,通过 load 方法实现了 iOS 微服务组件的自注册。再结合宏与 @compatibility_alias 完成了 iOS 的 " 注解 " 功能。达到 " 注解 " 实现 " 微服务 " 路由的效果:

@page ( @"main" ) - ( void ) dispatch { // Do stuff...}

Pbind 是一个支持 LiveLoad 的高度可配置化框架,以上代码实践均源自 Pbind 的开发过程,关于注解注册服务的部分还可以参考 Pbind 源码中的 PBAction 以及 PBClient 的实现。

以上内容由"CocoaChina"上传发布 查看原文
相关标签 ios路由apple新增

觉得文章不错,微信扫描分享好友

扫码分享