在SDK设计的时候,不注意自己是嵌入到别人的运行环境中的一部分。还是按照原先做App的思路去处理,可以随意的更改运行时的实例的属性,甚至是修改很多全局变量的值。结果就是污染了宿主的环境,导致宿主无法正常运行。

在设计SDK的时候应当注意到底点之污染问题(一)

运行时环境污染,内存污染,文件污染

切记不要污染宿主的环境,修改宿主环境中的变量

在处理百度钱包集成到手机百度的过程中,发现了一个奇怪的问题:

当进入钱包首页,再退出钱包的时候,手百中的手势滑动神奇的被禁掉了。

跟踪了半天,发现了问题所在。原来在钱包ViewController的基类,BaseVC中将self.navigationController.delegate设置成了nil。而钱包Push界面的时候,是用的手机百度传进来的navigationControoler。也就是说手机百度和钱包在公用一个UINavigationController的实例。这一下子所有原来的navigationController的delegate就真的成了nil了,尤其是推出之后。结果就导致了,手机百度写在delegate中的逻辑无法顺利执行,于是手百中的手势被干掉了。

这个问题,暴漏了一个非常常见的现象:

在SDK设计的时候,不注意自己是嵌入到别人的运行环境中的一部分。还是按照原先做App的思路去处理,可以随意的更改运行时的实例的属性,甚至是修改很多全局变量的值。结果就是污染了宿主的环境,导致宿主无法正常运行。

这个问题让我们回到了一个老生常谈的问题上:隔离。之前我们注意到的是接口隔离和职责隔离,讲究在设计类和模块的时候高内聚低耦合的确保各个类或者模块之间的影响越小越好。而现在我们,我们延伸一步,除了在设计和编码阶段的需要注意到隔离的问题。而且我们仍然需要考虑到,程序运行时其运行环境的隔离。尤其是对于SDK而言,其和宿主共享同一个运行时环境。稍有不慎,你修改了环境,或者说的更甚一些:污染了环境。将会导致宿主程序无法正常运行。因而需切记!

但是问题来了,有些时候SDK的确需要对环境做出一些修改,来让SDK的逻辑能够正常运行,那么我们可以怎么去处理?

备忘录模式,快照与还原

这是最直接的一个想法,既然要污染他的环境。修改他的变量,那么我可不可以在改动之前先做一个备份。等到推出SDK的时候进行还原呢。是啊,这是一个可行的方案。
但是要求SDK要有明确的入口和出口。这样才能够保证你能够在合适的地方进行快照,也在合适的出口的地方进行还原操作。在设计并实践该方法的时候,本着职责单一的原则,尽量将这个快照与还原的功能设计成一个单独的功能模块。尽量不要放在原有业务逻辑处理模块中。否则将会带来维护上的灾难。

这个问题在钱包中所也是有所体现,将设置self.navigationController.delegate的代码写在了所有VC的基类之类。这样,就导致了现在钱包所有的VC都会执行这段代码。假设我现在有这么几个类:

@interface BaseVC : ViewController
- (void) viewDidLoad
{
  [super viewDidLoad];
  self.navigationController.delegate = nil;
}
@end

@interface VC1 : BaseVC

@end

@interface VC2 : BaseVC

@end
........

这段一是嵌入到了业务逻辑当中,二来由于是在基类中,所以当程序运行时VC1,VC2被实例化之后,而又由同一个NavigtionController来push的话,那么navigationController的delegate将会被重置好多次。oy my god!这样就更难理解和维护。虽然你可以拆东墙补西墙,发现BUG之后,在这里再继续写多余的逻辑来保证delegate能够被设置正确。但是为什么不修改一次,彻底杜绝类似的BUG发生呢?

创建VitualEnviroment,搞一个自己的环境,不和宿主共享。

这里说的VitrualEnviroment没有像VM那么高大上,只是一个比较贴切的说法。就是搞一个自己的环境,不和宿主共享。这样就直接做到了内存上的隔离。就拿刚才一直说的navigationController的例子来说吧。就是我们创建一个自己的navigationController来使用,不去使用宿主的那个实例。这样无论我们怎样~self.navigationController.delegate = nil;~。也不会对原先的nav造成影响。让宿主出现莫名其妙的问题。

而这里所谓VitrualEnviroment的实现,主要是创建新实例避免与宿主混淆。其实现手段无非是

  1. 深拷贝
  2. 创建并初始化

内存隔离也是非常重要的一个事情啊。

重中之重,是要识别

其实,最关键的是,我们在改动的时候,需要识别出来,我们的改动是否对宿主的环境有所污染,是否修改了宿主的实例,是否修改了宿主的全局变量,是否动了宿主的某些文件。。。。。。重要的事情说三遍。一行代码虽小,影响却不知几何。尤其是在做SDK的时候,更要慎之又慎。考虑周全,从设计到编码,从编码到编译与链接,从连接到运行时,到整个变量的生命周期。

Last modification:April 7th, 2020 at 08:05 pm
如果觉得我的文章对你有用,请随意赞赏