Plug-in
Plug-in即插件,一种扩展程序新特性而不需要改变主程序代码的方式。
创建Plug-in结构
创建plug-in的三种方式:
- Plug-ins 实现一个正式协议
- Plug-ins 从一个非正式协议实现一些方法
- Plug-ins 继承一个抽象类或者具体基础类
发布一个正式Plug-ins协议
如果要使用自己声明的插件协议,只需要在头文件种声明协议即可。如下列:
/*
MyGreatImageApp
Graphics Filter Interface version 0
MyAppBitmapGraphicsFiltering.h
*/
#import <Cocoa/Cocoa.h>
@protocol MyGreatImageAppBitmapGraphicsFiltering
// Returns the version of the interface you're implementing.
// Return 0 here or future versions may look for features you don't have!
- (unsigned)interfaceVersion;
// Returns what to display in the Filter menu.
- (NSString *)menuItemString;
// The main worker bee: filters the bitmap and returns a modified version.
- (NSBitmapImageRep *)filteredImageRep:(NSBitmapImageRep *)imageRep;
// Returns the window controller for the settings configuration window.
- (NSWindowController *)configurationWindowController;
@end
发布一个非正式Plug-ins协议
非正式协议比正式协议要复制一点,主要是有些方法是可选的,有些方法是必须,例子如下:
/*
MyGreatImageApp
Graphics Filter Interface version 0
MyAppBitmapGraphicsFiltering.h
*/
#import <Cocoa/Cocoa.h>
@interface NSObject(MyGreatImageAppBitmapGraphicsFiltering)
// REQUIRED
// Returns the version of the interface you're implementing.
// Return 0 here or future versions may look for features you don't have!
- (unsigned)interfaceVersion;
// OPTIONAL
// Returns what to display in the Filter menu. Defaults to the plug-in
// filename without the extension.
- (NSString *)menuItemString;
// REQUIRED
// The main worker bee: filters the bitmap and returns a modified version.
- (NSBitmapImageRep *)filteredImageRep:(NSBitmapImageRep *)imageRep;
// OPTIONAL
// Returns the window controller for the settings configuration window.
// If this method is not implemented, no Settings option is provided.
- (NSWindowController *)configurationWindowController;
@end
发布一个基础类Plug-in接口
如果你想提供一个功能给所有的Plug-in可以发布一个基础类给所有的Plug-in去继承,如下例子:
声明:
#import <Cocoa/Cocoa.h>
@interface MyAppEmbeddingView : NSView
{
@private
NSURL *_URL;
void *_reserved1;
void *_reserved2;
void *_reserved3;
}
- (id)initWithFrame:(NSRect)frameRect URL:(NSURL)URL;
- (unsigned)interfaceVersion;
- (NSURL *)URL;
- (void)setURL:(NSURL *)URL;
@end
实现:
#import "MyAppEmbeddingView.h"
@implementation MyAppEmbeddingView
- (id)initWithFrame:(NSRect)frameRect URL:(NSURL)URL
{
self = [self initWithFrame:frameRect];
[self setURL:URL];
return self
}
- (unsigned)interfaceVersion
{
return 0;
}
- (void)drawRect:(NSRect)rect
{
NSEraseRect(rect);
}
- (NSURL *)URL
{
return _URL;
}
- (void)setURL:(NSURL *)URL
{
[URL retain];
[_URL release];
_URL = URL;
}
@end
加载Plug-in
加载插件的步骤:
- 在标准位置找到插件
- 加载每个插件的可执行代码
- 通过插件接口验证插件
- 实例化插件
验证Plug-in
如果是正式协议插件,需要检查实现该协议的类有没有都实现该协议的方法即可,代码如下:
- (BOOL)plugInClassIsValid:(Class)plugInClass
{
if([plugInClass
conformsToProtocol:@protocol(MyGreatImageAppBitmapFiltering)])
{
if([plugInClass instancesRespondToSelector:
@selector(interfaceVersion)] &&
[plugInClass instancesRespondToSelector:
@selector(menuItemString)] &&
[plugInClass instancesRespondToSelector:
@selector(filteredImageRep)] &&
[plugInClass instancesRespondToSelector:
@selector(configurationWindowController])
{
return YES;
}
}
return NO;
}
对于非正式协议更简单,只需要检查必须的方法即可,不用检查可选方法,代码如下:
- (BOOL)plugInClassIsValid:(Class)plugInClass
{
if([plugInClass instancesRespondToSelector:
@selector(interfaceVersion)] &&
[plugInClass instancesRespondToSelector:
@selector(filteredImageRep)])
{
return YES;
}
return NO;
}
继承基础类的检查只需要判断实现类的父类即可,代码如下:
- (BOOL)plugInClassIsValid:(Class)plugInClass
{
if([plugInClass isSubclassOfClass:[MyAppEmbeddingView class]])
{
return YES;
}
return NO;
}
加载Plug-in
NSString *ext = @"plugin";
NSString *appSupportSubpath = @"Application Support/KillerApp/PlugIns";
// ...
- (void)loadAllPlugins
{
NSMutableArray *instances;
NSMutableArray *bundlePaths;
NSEnumerator *pathEnum;
NSString *currPath;
NSBundle *currBundle;
Class currPrincipalClass;
id currInstance;
bundlePaths = [NSMutableArray array];
if(!instances)
{
instances = [[NSMutableArray alloc] init];
}
[bundlePaths addObjectsFromArray:[self allBundles]];
pathEnum = [bundlePaths objectEnumerator];
while(currPath = [pathEnum nextObject])
{
currBundle = [NSBundle bundleWithPath:currPath];
if(currBundle)
{
currPrincipalClass = [currBundle principalClass];
if(currPrincipalClass &&
[self plugInClassIsValid:currPrincipalClass]) // Validation
{
currInstance = [[currPrincipalClass alloc] init];
if(currInstance)
{
[instances addObject:[currInstance autorelease]];
}
}
}
}
}
- (NSMutableArray *)allBundles
{
NSArray *librarySearchPaths;
NSEnumerator *searchPathEnum;
NSString *currPath;
NSMutableArray *bundleSearchPaths = [NSMutableArray array];
NSMutableArray *allBundles = [NSMutableArray array];
librarySearchPaths = NSSearchPathForDirectoriesInDomains(
NSLibraryDirectory, NSAllDomainsMask - NSSystemDomainMask, YES);
searchPathEnum = [librarySearchPaths objectEnumerator];
while(currPath = [searchPathEnum nextObject])
{
[bundleSearchPaths addObject:
[currPath stringByAppendingPathComponent:appSupportSubpath]];
}
[bundleSearchPaths addObject:
[[NSBundle mainBundle] builtInPlugInsPath]];
searchPathEnum = [bundleSearchPaths objectEnumerator];
while(currPath = [searchPathEnum nextObject])
{
NSDirectoryEnumerator *bundleEnum;
NSString *currBundlePath;
bundleEnum = [[NSFileManager defaultManager]
enumeratorAtPath:currPath];
if(bundleEnum)
{
while(currBundlePath = [bundleEnum nextObject])
{
if([[currBundlePath pathExtension] isEqualToString:ext])
{
[allBundles addObject:[currPath
stringByAppendingPathComponent:currBundlePath]];
}
}
}
}
return allBundles;
}