使用类别扩展 iTunesApplication 类

发布于 2024-10-01 03:53:27 字数 746 浏览 0 评论 0原文

我刚刚学习如何使用 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,所以我愿意接受更好的答案,但现在这就是我所拥有的。

I am just learning how to use ScriptingBridges. I made a method that slowly fades the volume on iTunes, and would like to make it a category so I can do the following:

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

I made another category for NSSpeechSynthesizer that works, but I can't get this one to. I keep getting the following build error:

"_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

Is there something special I can do to make it work since I can't include the symbols?

Thanks,
Ryan Pendleton

UPDATE:
I only found one solution, which is below. It involves MethodSwizzling, so I'm open to better answers, but for now it's all I have.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

甜是你 2024-10-08 03:53:29

我找到的解决方案是使用 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];
}

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

The solution I found was to use the Objective-C runtime API. I'm sure there's a better way to organize this, but here's how I did it:

Here are my .h and .m files for creating the category. Notice how lowerVolume is not an actual method, but a C function with the arguments id self, and SEL _CMD. You'll also notice a setupCategories function. We'll call that later.

// 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");
}

Now that I've made the functions, I need to actually add them to the scripting bridge class using the Objective-C runtime API. I'll do this in main.m to make sure that the methods are ready to be used when the run loop starts.

// 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];
}

Now, I can use my method wherever I want as long as I include the header files:

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

If any of this doesn't make sense, just tell me and I'll try to explain it better.

没企图 2024-10-08 03:53:29

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

I think you need to include -framework ScriptingBridge to your gcc arguments. That got it to compile for me!

错々过的事 2024-10-08 03:53:29

如上所述,您无法轻松地在 iTunesApplication 上创建类别,因为它在编译时不存在,而且运行时类名是 ITunesApplication(大写“I”)。

我发现的最好的解决方案是在确实存在的类 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];
}

As noted above, you can't easily do a category on iTunesApplication because it doesn't exist at compile time, and also because the runtime class name is ITunesApplication (capital "I").

The best solution I've found is to do your category on the class that DOES exist, SBApplication. Here's the code I tested that works and does what the original example was trying to do:

//  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];
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文