依赖注入实现内存释放

本文介绍了一种名为依赖注入(Dependency Injection)的设计模式,并使用这种模式释放不必一直持有的对象,用来达到释放内存的效果。

什么是依赖注入

举个栗子:

“When you go and get things out of the refrigerator for yourself, you can cause problems. You might leave the door open, you might get something Mommy or Daddy doesn’t want you to have. You might even be looking for something we don’t even have or which has expired.

What you should be doing is stating a need, “I need something to drink with lunch,” and then we will make sure you have something when you sit down to eat.”

以上答案来自Stack Overflow,大概意思是,对于一个5岁大的小朋友来说,从冰箱里取东西出来是一件危险的事情,你可能忘记关冰箱门,或者取出爸爸的伏特加,也可能寻找根本不存在的食物或者用舌头舔冷冻室的铁栏杆……

因此,这件事的解决办法就是,对你的父母说:“我想在吃午饭时喝点什么”,然后父母就会拿出一些喝的给你。

换成程序员能听懂的话就是:高层类(5岁小孩)应该依赖底层基础设施(家长)来提供必要的服务。

依赖注入是一个将行为从依赖中分离的技术,简单地说,它允许开发者定义一个方法函数依赖于外部其他各种交互,而不需要编码如何获得这些外部交互的实例。 这样就在各种组件之间解耦,从而获得干净的代码,相比依赖的硬编码, 一个组件只有在运行时才调用其所需要的其他组件,因此在代码运行时,通过特定的框架或容器,将其所需要的其他依赖组件进行注入,主动推入。

为什么需要依赖注入

依赖注入框架的运用可以帮我们将APP的设计分割成好几个模块,分给不同的开人员,当完成开发之后再进行合并充分解决了团队之间模块化分工的不足。
借用objccn.io的话说:

我最初决定钻研DI是因为在执行测试驱动开发 (TDD),而在 TDD 的过程中有一个很纠结的问题会时常跳出来:“对于这个实现,如何编写单元测试?”。后来我发现其实 DI 本身是在彰显一个更高层面的概念:代码组成了模块,模块拼接构建成了应用本身。

如何实现依赖注入

以下例子来自objeccn.io

构造器注入

构造器注入,即将某个依赖对象传入到构造器中 (在 Objective-C中指 designated 初始化方法) 并存储起来,以便在后续过程中使用:

- (NSNumber *)nextReminderId
{
    NSNumber *currentReminderId = [self.userDefaults objectForKey:@"currentReminderId"];
    if (currentReminderId) {
        currentReminderId = @([currentReminderId intValue] + 1);
    } else {
        currentReminderId = @0;
    }
    [self.userDefaults setObject:currentReminderId forKey:@"currentReminderId"];
    return currentReminderId;
}

属性注入

对于属性注入,nextReminderId 的代码看起来和 self.userDefaults 的做法是一致的。只是这次不是将依赖对象传递给初始化方法,而是采用属性赋值方式:

@interface Example
@property (nonatomic, strong) NSUserDefaults *userDefaults;
- (NSNumber *)nextReminderId;
@end

现在可以在单元测试中创建一个对象,然后将需要的东西通过对 userDefaults 属性进行赋值。但是要是这个属性没有被预先设定的话要怎么办呢?这时,我们可以使用 lazy 加载的方法为其设置一个适当的默认值,这能保证始终可以通过 getter 拿到一个确切的值:

- (NSUserDefaults *)userDefaults
{
    if (!_userDefaults) {
        _userDefaults = [NSUserDefaults standardUserDefaults];
    }
    return _userDefaults;
}

这样的话,对 userDefaults 来说,如果在使用者取值之前做过赋值操作,那么从 self.userDefaults 得到的就是通过 setter 赋的值。如果这个属性在使用前未被赋值,从 self.userDefaults 得到的就是[NSUserDefaults standardUserDefaults]

方法注入

如果依赖对象只在某一个方法中被使用,则可以利用方法参数做注入:

- (NSNumber *)nextReminderIdWithUserDefaults:(NSUserDefaults *)userDefaults
{
    NSNumber *currentReminderId = [userDefaults objectForKey:@"currentReminderId"];
    if (currentReminderId) {
        currentReminderId = @([currentReminderId intValue] + 1);
    } else {
        currentReminderId = @0;
    }
    [userDefaults setObject:currentReminderId forKey:@"currentReminderId"];
    return currentReminderId;
}

再一次说明,这样看起来可能会很奇怪,并不是所有的例子中 NSUserDefaults作为依赖都显得恰如其分。比如说这个例子中,如果使用 NSDate 做注入参数传入可能更会彰显其特点。

如何实现内存释放

上面提到的几种实现依赖注入的方法中,可以用来释放不必一直持有的对象的方法是属性注入。如果在一个对象的生命周期中,有什么属性是可以随时从文件系统中(或是其他方法)恢复的,那就让我们注入它吧!

举个例子,设备横屏时显示红色按钮redButton,设备竖屏时显示绿色按钮greenButton,同时两个button的行为完全一致,对外暴露的接口可以统一为一个button

// 这是对外接口,属性注入
- (UIButton *)button
{
    if(UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
        return self.redButton;
    }
    else {
        return self.greenButton;
    }
}

// 继续注入两个button
- (UIButton *)redButton
{
    if(!_redButton) {
        _redButton = [UIButton new];
        // do sth.
    }
    return _redButton;
}
- (UIButton *)greenButton
{
    if(!_greenButton) {
        _greenButton = [UIButton new];
        // do sth.
    }
    return _greenButton;
}

设备处于某个方向时,同时只有一个button被显示,因此可以释放掉不被显示的一个以节约内存

- (UIButton *)removeHiddenButton
{
    if(UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
        [_greenButton removeFromSuperView];
        _greenButton = nil;
    }
    else {
        [_redButton removeFromSuperView];
        _redButton = nil;
    }
}
- (void)layoutSubviews
{
    [self button];
    [self removeHiddenButton];
}

属性注入保证了调用者(高级类)不必关心被调用对象的初始化情况,所以即使被调用对象已经释放,通过调用注入接口,仍然可以在需要时生成。利用这一特性,我们可以在收到内存警告时释放掉暂时不用的对象,同时也必须注意,被释放的对象必须是可恢复的

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,458评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,030评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,879评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,278评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,296评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,019评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,633评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,541评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,068评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,181评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,318评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,991评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,670评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,183评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,302评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,655评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,327评论 2 358

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,678评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,226评论 25 707
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,154评论 30 470
  • 妈妈只是一普通农民。有一次,大家聚一桌吃饭。觥筹交错间,有人对爸妈说:你们也该出去浪漫一下了。我妈随即接道...
    西北蛮荒女阅读 203评论 0 1
  • 一.多线程的优点: 1.能适当提高程序的执行效率 2.能提高资源的利用率 3.线程上的任务执行完毕后,线程会自动销...
    Cat_uncle阅读 285评论 0 0