使用Categories扩展iTunesApplication类

时间:2010-11-07 02:01:27

标签: objective-c xcode objective-c-runtime scripting-bridge objective-c-category

我正在学习如何使用ScriptingBridges。我制作了一种方法,可以慢慢淡化iTunes上的音量,并希望将其作为一个类别,以便我可以执行以下操作:

iTunesApplication* iTunes = [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];
[iTunes lowerVolume:50 speed:1];

我为NSSpeechSynthesizer制作了另一个有效的类别,但是我无法将这个类别用到。我一直收到以下构建错误:

"_OBJC_CLASS_$_iTunesApplication", referenced from:
l_OBJC_$_CATEGORY_iTunesApplication_$_iTunesApplicationAdditions in iTunesApplication.o
objc-class-ref-to-iTunesApplication in iTunesApplication.o
ld: symbol(s) not found
collect2: ld returned 1 exit status

由于我不能包含这些符号,我能做些什么特别的工作吗?

谢谢,
瑞恩彭德尔顿

更新 我只找到了一个解决方案,如下所示。它涉及MethodSwizzling,所以我愿意接受更好的答案,但就目前而言,这就是我的全部。

3 个答案:

答案 0 :(得分:1)

我发现的解决方案是使用Objective-C运行时API。我确信有一个更好的方法来组织这个,但这就是我如何做到的:

以下是我用于创建类别的.h和.m文件。请注意lowerVolume不是实际方法,而是具有参数id selfSEL _CMD的C函数。您还会注意到setupCategories功能。我们稍后再说。

// iTunes+Volume.h

#import <objc/runtime.h>
#import "iTunes.h"

void lowerVolume(id self, SEL _cmd, int dest, float speed);
void setupCategories();

@interface iTunesApplication (Volume)

- (void)lowerVolume:(int)dest speed:(float)speed;

@end

// iTunes+Volume.m

#import "iTunes+Volume.h"

void lowerVolume(id self, SEL _cmd, int dest, float speed)
{
    NSLog(@"Lower Volume: %i, %f", dest, speed);
}

void setupCategories()
{
    id object = [[SBApplication alloc] initWithBundleIdentifier:@"com.apple.iTunes"];
    Class class = [object class];
    [object release];

    class_addMethod(class, @selector(lowerVolume:speed:), (IMP)lowerVolume, "@:if");
}

现在我已经完成了这些功能,我需要使用Objective-C运行时API将它们实际添加到脚本桥类中。我将在main.m中执行此操作,以确保在运行循环开始时可以使用这些方法。

// main.m

#import <Cocoa/Cocoa.h>
#import "iTunes+Volume.h"

int main(int argc, char *argv[])
{
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

    setupCategories();
    return NSApplicationMain(argc,  (const char **) argv);

    [pool drain];
}

现在,只要我包含头文件,我就可以在任何我想要的地方使用我的方法:

- (void)mute
{
    iTunesApplication* iTunes = [[SBApplication alloc] initWithBundleIdentifier:@"com.apple.iTunes"];
    [iTunes lowerVolume:0 speed:1];
    [iTunes release];
}

如果其中任何一个没有意义,请告诉我,我会尝试更好地解释它。

答案 1 :(得分:0)

我认为您需要将-framework ScriptingBridge包含在您的gcc参数中。这让它为我编译!

答案 2 :(得分:0)

如上所述,您无法轻松地在iTunesApplication上执行某个类别,因为它在编译时不存在,并且还因为运行时类名称是ITunesApplication(资本&#34; I&#34; )。

我发现的最佳解决方案是在已存在的类SBApplication上进行分类。这是我测试过的代码,它可以完成原始示例尝试执行的操作:

//  SBApplication+Extensions.h

@import ScriptingBridge;

@interface SBApplication (Extensions)

- (void)lowerVolume:(int)dest speed:(float)speed;

@end

//  SBApplication+Extensions.m

#import "iTunes.h"
#import "SBApplication+Extensions.h"

@implementation SBApplication (Extensions)

- (void)lowerVolume:(int)dest speed:(float)speed
{
    NSLog(@"Lower Volume: %i, %f", dest, speed);
}

@end

// Caller, say in AppDelegate

#import "SBApplication+Extensions.h"

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{

    iTunesApplication *iTunesApp =
        [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];

    [iTunesApp lowerVolume:4 speed:3.3f];
}