[Cocoa]_[Plug-in]

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

加载插件的步骤:

  1. 在标准位置找到插件
  2. 加载每个插件的可执行代码
  3. 通过插件接口验证插件
  4. 实例化插件

验证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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值