博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS实现依赖注入
阅读量:6981 次
发布时间:2019-06-27

本文共 2894 字,大约阅读时间需要 9 分钟。

依赖注入(Dependency Injection)这个词,源于java,但在Cocoa框架中也是十分常见的。

举例来说:
UIView的初始化方法initWithFrame

- (id)initWithFrame:(CGRect)frame NS_DESIGNATED_INITIALIZER;

这里的frame传入值,就是所谓的依赖(Dependency),这个View实例化是根据frame注入实现的。

但这种用法有很大的局限性

  1. 我们不知道究竟依赖注入的属性有哪些

  2. 不可能无限加长方法长度来满足更多的依赖属性

所以我们准备采用字典容器对NSObject类进行依赖注入扩展。

给NSObject类添加一个Category

@interface NSObject (XXXDependencyInjection)- (nullable id)initWithParams:(nonnull NSDictionary *)params;- (void)injection:(nonnull NSDictionary*)params;@end

实现注入方法

- (id)initWithParams:(NSDictionary *)params{    self = [self init];    if (self) {        [self injection:params];    }    return self;}- (void)injection:(NSDictionary*)params{    [params.allKeys enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {        SEL selector = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:",[[obj substringToIndex:1] uppercaseString],[obj substringFromIndex:1]]);        id value = [params objectForKey:obj];                        if ([self respondsToSelector:selector]) {#pragma clang diagnostic push#pragma clang diagnostic ignored "-Warc-performSelector-leaks"            [self performSelector:selector withObject:value];#pragma clang diagnostic pop        }        else        {            @try {                [self setValue:value forKeyPath:obj];            }            @catch (NSException *exception) {                NSLog(@"%@",exception);                [exception raise];            }            @finally {                            }        }    }];}

解释

我们将需要注入的属性,封装到一个字典里,例如:

UIViewController* controller = [[UIViewController alloc] initWithParams:@{                               @"title":@"测试",                               @"view.backgroundColor":[UIColor whiteColor]                                                                              }];

我们给这个VC注入了两个属性,一个是其title,一个是其View的backgroundColor属性。

字典传入以后,我们读区params.allKeys进行遍历,拼装set+参数名的selector,这里用的是NSSelectorFromString方法:

SEL selector = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:",[[obj substringToIndex:1] uppercaseString],[obj substringFromIndex:1]]);

然后我们判断实例是否可以响应这个set方法,如果可以,则给其赋值。

if ([self respondsToSelector:selector]) {#pragma clang diagnostic push#pragma clang diagnostic ignored "-Warc-performSelector-leaks"            [self performSelector:selector withObject:value];#pragma clang diagnostic pop        }

这里的三行clang宏是为了消除编译器的内存泄漏警告,这里因为我们进行了验证,所以不会出现leak。

KVC实现跨实例赋值

我们注意到上例中还有一句给VC的View改变背景颜色

@"view.backgroundColor":[UIColor whiteColor]

这里就用到了KVC的点语法特性,在我们判断到实例不能响应 if ([self respondsToSelector:selector]) 的时候,通过点语法,进行赋值

@try {    [self setValue:value forKeyPath:obj];}@catch (NSException *exception) {    NSLog(@"%@",exception);   [exception raise];}@finally {}

这里添加了异常捕获,因为点语法对属性名称拼写要求是全匹配,否则抛异常,所以要注意。

优缺点

这样改造过的init方法,优点非常明显,就是绑定更加集中便捷,如果使用的是storyboard则可以轻松实现前后端分离。

目前的缺点也很明显,不能告诉开发者哪些属性是必需依赖,另外还不能支持非对象属性的赋值,希望抛砖引玉,大家来改进这段代码。

转载地址:http://tgjpl.baihongyu.com/

你可能感兴趣的文章
面试题2:二维数组中的查找
查看>>
文件上传的渐进式增强
查看>>
leetcode -- Sort Colors
查看>>
C#中使用自定义的纸张大小
查看>>
1z0-052 q209_3
查看>>
行测题哦
查看>>
JavaScript Window Navigator 浏览器本身的信息
查看>>
使用Android Ant在编译时混淆
查看>>
通过Servlet 将服务器硬盘图片 展示到浏览器
查看>>
linux_nand_driver
查看>>
语义化的软件版本号规则,你是否真的了解软件的版本号
查看>>
[通俗易懂]理解“委托”
查看>>
sdut 2162:The Android University ACM Team Selection Contest(第二届山东省省赛原题,模拟题)...
查看>>
xocodebulid 自动化打包 解决提示 ld: library not found for -lPods 问题
查看>>
LPEG
查看>>
python none,null,,,,,类型
查看>>
HDU 3360 National Treasures 奇偶匹配的最低点覆盖
查看>>
/lib /usr/lib /usr/local/lib区别
查看>>
HDU - 5008 Boring String Problem (后缀数组+二分法+RMQ)
查看>>
Swift - 实现点击UITableView单元格时自动展开单元格
查看>>