首先,了解一下插件开发的应用价值。
最重要的一点,就是可以使软件的内容不僵化。由于插件可以通过网络进行传输,并且实时的展示出来(感谢cocoa的动态特性),因而理论上(极端情况),软件可以只是一个空架子,所有软件内容都从服务器上下载。【注:至今为止,我所在的项目都是使用的企业账号,因而不知道此种插件模式是否可以上传到APPSTORE。】或者,可以对软件进行动态换肤,或者提供一种持续开发的实现手段,等等。
其次,这种方式可以使软件开发流程具有更大的灵活性。例如,不同的团队进行并行开发。每个团队开发一个插件,最后进行拼装。由于插件所具有的隐藏内部实现的特性会使软件更健壮。而团队之间是看不见各自源代码的,这对于外包(苦B?)来说是很有趣的一种方式,即将不重要的或是一些体力劳动插件交予合作方进行开发。而只要接口订的好,完全可以实现并行开发,提高软件开发的速度。
一、基础知识
1、bundle(包)
bundle(包)是cocoa开发中提供的一种文件存储、组织方式。
右键点击bundle文件,选择“显示包内容”,可以看到里面包含的内容。实际上,它就是一个文件夹,在windows下可以直接打开它(IOS应用.APP实际上也是bundle,即程序中的mainBundle)。
bundle中可以包含很多内容,包括各种资源,甚至编译好的源代码。通过它,可以动态的做很多事情。
2、面向接口
高内聚低耦合一直是面向对象开发的追求目标之一,而面向接口则是使软件接近这种目标的方法之一。模块之间的交互都是面向接口的,则模块内做出的改变不会影响其他模块,从而提高了软件的健壮程度。在Objective-C中,此种思想是使用协议来完成的,即protocol。
@protocol A
-(void) methodA;
@end
两个模块之间定义各自需要的协议,通过这些接口进行交互。这在插件开发中是必须使用的方法之一。
二、具体实现
首先,有几个API需要了解。
主程序所在bundle:
NSBundle *mainBundle = [NSBundle mainBundle];
如果有路径,则使用下面代码获得bundle:
NSBundle *aBundle;
aBundle= [NSBundle bundleWithPath:@"..."];
另,两个重要的路径:
1、mainBundle(即APP)路径:
NSString * resPath = [[NSBundle mainBundle] resourcePath];
2、Document及其子路径:
NSArray * documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * pluginPath = [[documentPath objectAtIndex:0] stringByAppendingPathComponent:@"..."];
获得bundle之后,就可以访问其中的任何资源了。但是,由于我们希望尽可能的实现高内聚低耦合,因而我们希望外界对于bundle内的东西了解的越少越好。每个bundle都有一个plist,其中有一个字段,叫做principal class,可以使用下面的代码进行获得:
Class aClass = [aBundle principalClass];
id anInstance = [[aClass alloc] init];
这个对象,将它设计的符合某个协议,无疑是很好的接口类。使用下面的代码进行某些功能的执行:
if([anInstance conformsToProtocol:@protocol(...)])
{
[anInstance performSelector:@selector(...)];
}
另外,对于bundle中UIViewController的载入,我发现只能使用initWithNibName: bundle: ,否则载入的窗口无法展示。对于bundle在xcode中的开发,我会另起一篇文章进行讲解。
总体来说,整个过程如下所示:
1、 定义插件内外的所具备的接口,用于交互。比如,外部符合协议A,插件符合协议B,则一般来说,principalClass符合协议B,并且应该具有一个方法类似这样:
-(id)initWithDelegate:(id<A>)delegate;
这样,插件内外就达成了协议,可以进行交互了。这个过程实际上很重要,因为一旦接口经常改变,牵连的东西太多,这里就不再赘述了。
2、 各个团队并行开发,有的开发主程序,有的开发插件。可以采取网上下载的方式,也可以开发完合在一起。
联系客服