架构设计中的循环引用

引用计数是一种经典的内存管理垃圾回收机制,但它最明显的副作用就是循环引用,导致内存泄漏。循环引用其实是一个闭环。

闭环是什么

从图论的角度来说,闭环,其实就是一个有向有环图。图中的顶点表示一个对象,每一条边表示对象之间的关系。若图中的每条边都是有方向的,则称为有向图。
如果图中存在一个顶点,从该顶点出发经过若干条边可以回到该点,则这个图是一个有向有环图。这个环我们暂且称它为闭环。

除了引用计数内存管理,在工程应用中,很多地方都存在着闭环的影子:

  • 死锁,是指多个进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。死锁的形成必然存在着资源的环形链,这也是一个闭环。

闭环的检测

判断一个有向图是否存在环,有多种算法。

  • 算法1:环中的每个顶点的度都是>=2,基于此可以找到环:
    1. 求出图中所有顶点的度,
    2. 删除图中所有度<=1的顶点以及与该顶点相关的边,把与这些边相关的顶点的度减一
    3. 如果还有度<=1的顶点重复步骤2
    4. 最后如果还存在未被删除的顶点,则表示有环;否则没有环
  • 算法2: 利用深度优先搜索遍历该图,如果在遍历的过程中,发现某个节点有一条边指向同一棵生成树上的祖先节点,则表示存在环:


    dfs.jpg

facebook最近刚开源的FBRetainCycleDetector就是使用深度优先搜索来检测iOS应用在运行时是否存在循环应用。它利用ivar layout 找出对象所引用的所有对象,并用有向图表示,节点就是对象,边就是对象之间的引用。然后使用深度优先搜索,检测出其中是否存在循环引用,精简过的核心代码如下:

[stack addObject:wrappedObject];
while ([stack count] > 0) {
  @autoreleasepool {
    FBNodeEnumerator *top = [stack lastObject];
    [objectsOnPath addObject:top];
    FBNodeEnumerator *firstAdjacent = [top nextObject];
    if (firstAdjacent) {
      BOOL shouldPushToStack = NO;
      if ([objectsOnPath containsObject:firstAdjacent]) {
       // 如果路径中存在访问过的点,说明存在环,表明有内存泄漏隐患
      } else {
        shouldPushToStack = YES;
      }
      if (shouldPushToStack) {
          [stack addObject:firstAdjacent];
      }
    } else {
      [stack removeLastObject];
      [objectsOnPath removeObject:top];      
    }
  }
}

架构设计应避免闭环

架构的设计当中,大部分场景下, 应当避免闭环。模块之间的关系应当是线性的,或者树状的,而不应该是存在闭环的复杂网络状。树状的结构意味着系统的层次清晰,模块职责明确。典型的在线服务三层架构图如下:

arch_3.jpg

这就是一个很简单的树状图,模块之间划分得非常清晰。流行的MVC和MVVP其实也是帮我们梳理这种层次结构。MVC三者的引用关系如下(Controller引用View和Model,View引用Model):

mvc.jpg

这是一个无环图,假设在图中增加一条从Model到Controller的边,就形成了一个闭环。如果你的Model层或者View层代码中出现了类似这样的代码:import "Controller.h",请再审视一遍你的代码设计。
模块之间相互引用,意味着模块之间耦合性高,设计的抽象还不够。在日常的实践开发,这种相互引用的引入往往是无心而隐蔽的。但是随着代码库的日益庞大,模块之间的联系日益增多,一旦这种循环引用多了,整个系统的结构将变得无比复杂,杂乱无章。最终系统的结构会变成这样:
messy_arch.jpg

这个系统一旦出现bug,将牵一发而动全身,改bug就是拆了东墙补西墙,久而久之,再也没有人改动这坨代码,项目华丽地走上了推倒重写之路。

那么如何找到模块之间是否存在闭环呢?我们可以将问题简化成为文件之间是否存在循环import。这样可以写一个检测脚本,协助定位和清理模块之间的关系。

  1. 遍历项目中所有的文件,看每个文件import了哪些其他的文件。每个文件就是一个节点,每次import表示一次引用关系,从而建立一条有向边。
  2. 建立好有向图之后,使用深度优先搜索算法检测图中是否存在环,若存在,将引用路径打印出来。

<b>让代码库变得庞大并不难,难的是在业务发展过程中,一直保持架构的简单。牢记KISS原则:Keep It Simple and Stupid.</b>

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,392评论 25 707
  • 图是一种比线性表和树更复杂的数据结构,在图中,结点之间的关系是任意的,任意两个数据元素之间都可能相关。图是一种多对...
    Alent阅读 2,279评论 1 22
  • https://zh.visualgo.net/graphds 浅谈图形结构https://zh.visualgo...
    狼之独步阅读 4,113评论 0 0
  • 第一章 绪论 什么是数据结构? 数据结构的定义:数据结构是相互之间存在一种或多种特定关系的数据元素的集合。 第二章...
    SeanCheney阅读 5,734评论 0 19
  • 1 “我说,我要时小念,我宫欧就要她一个人!你就是找人再轮她一百遍,我还是只要她!你就是找人把她拆成一堆骨...
    沫鹿阅读 1,447评论 0 0