2

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

 2 years ago
source link: https://limboy.me/posts/use-objection-to-decouple-ios-project/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

2014-04-15

objection 是一个轻量级的依赖注入框架,受Guice的启发,Google Wallet 也是使用的该项目。「依赖注入」是面向对象编程的一种设计模式,用来减少代码之间的耦合度。通常基于接口来实现,也就是说不需要 new 一个对象,而是通过相关的控制器来获取对象。2013 年最火的 PHP 框架 laravel 就是其中的典型。

假设有以下场景:ViewControllerA.view 里有一个 button,点击之后 push 一个 ViewControllerB,最简单的写法类似这样:

- (void)viewDidLoad
{
    [super viewDidLoad];
    UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
    button.frame = CGRectMake(100, 100, 100, 30);
    [button setTitle:@"Button" forState:UIControlStateNormal];
    [button addTarget:self action:@selector(buttonTapped) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
}

- (void)buttonTapped
{
	ViewControllerB *vc = [[ViewControllerB alloc] init];
    [self.navigationController pushViewController:vc animated:YES];
}

这样写的一个问题是,ViewControllerA 需要 import ViewControllerB,也就是对 ViewControllerB 产生了依赖。依赖的东西越多,维护起来就越麻烦,也容易出现循环依赖的问题,而 objection 正好可以处理这些问题。

实现方法是:先定义一个协议(protocol),然后通过 objection 来注册这个协议对应的 class,需要的时候,可以获取该协议对应的 object。对于使用方无需关心到底使用的是哪个 Class,反正该有的方法、属性都有了(在协议中指定)。这样就去除了对某个特定 Class 的依赖。也就是通常所说的「面向接口编程」。

JSObjectionInjector *injector = [JSObjection defaultInjector]; // [1]
UIViewController <ViewControllerAProtocol> *vc = [injector getObject:@protocol(ViewControllerAProtocol)]; // [2]
vc.backgroundColor = [UIColor lightGrayColor]; // [3]
UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:vc];
self.window.rootViewController = nc;
  • [1] 获取默认的 injector,这个 injector 已经注册过ViewControllerAProtocol了。
  • [2] 获取ViewControllerAProtocol对应的 Object。
  • [3] 拿到 VC 后,设置它的某些属性,比如这里的 backgroundColor,因为在ViewControllerAProtocol里有定义这个属性,所以不会有 warning。

可以看到这里没有引用 ViewControllerA。再来看看这个ViewControllerAProtocol是如何注册到 injector 中的,这里涉及到了Module,对 Protocol 的注册都是在 Module 中完成的。Module 只要继承JSObjectionModule这个 Class 即可。

@interface ViewControllerAModule : JSObjectionModule
@end

@implementation ViewControllerAModule
- (void)configure
{
    [self bindClass:[ViewControllerA class] toProtocol:@protocol(ViewControllerAProtocol)];
}
@end

绑定操作是在configure方法里进行的,这个方法在被添加到 injector 里时会被自动触发。

JSObjectionInjector *injector = [JSObjection defaultInjector]; // [1]
injector = injector ? : [JSObjection createInjector]; // [2]
injector = [injector withModule:[[ViewControllerAModule alloc] init]]; // [3]
[JSObjection setDefaultInjector:injector]; // [4]
  • [1] 获取默认的 injector
  • [2] 如果默认的 injector 不存在,就新建一个
  • [3] 往这个 injector 里注册我们的 Module
  • [4] 设置该 injector 为默认的 injector

这段代码可以直接放到 + (void)load里执行,这样就可以避免在 AppDelegate 里 import 各种 Module。

因为我们无法直接获得对应的 Class,所以必须要在协议里定义好对外暴露的方法和属性,然后该 Class 也要实现该协议。

@protocol ViewControllerAProtocol <NSObject>
@property (nonatomic) NSUInteger currentIndex;
@property (nonatomic) UIColor *backgroundColor;
@end

@interface ViewControllerA : UIViewController <ViewControllerAProtocol>
@end

通过 objection 实现依赖注入后,就能更好地实现 SRP(Single Responsibility Principle),代码更简洁,心情更舒畅,生活更美好。拿 Pinterest 来说,下面的页面就可以划分为 3 个 Section。

demo_4_objection.png

各个 Section 可以由不同的人负责,然后串到一起就行,也能一定程度地避免 MVC(Mess View Controller)的出现。

总体来说,这个 lib 还是挺靠谱的,已经维护了两年多,也有一些项目在用,对于提高开发成员的效率也会有不少的帮助,可以考虑尝试下。

---- update (2014-04-30) ----

写了个壁纸的 demo,https://github.com/limboy/bizhi


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK