简体   繁体   中英

Linking problems with a category on Scripting Bridge

I am trying to write a category over iTunesTrack with associated objects (an NSMutableDictionary and an NSNumber)

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

@interface iTunesTrack (dictionary)
- (NSMutableDictionary*) getDictionary;
- (NSNumber*) getScan;
- (BOOL)scanTrack:(NSString *)equationString;
@end

This fails:

Undefined symbols for architecture x86_64:
  "_OBJC_CLASS_$_iTunesTrack", referenced from:
      l_OBJC_$_CATEGORY_iTunesTrack_$_dictionary in iTunesTrack+dictionary.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I have double checked that the Scripting Bridge framework is added and that the iTunesTrack+dictionary.m file is attached to the target. Could this be some error with combining Categories with Scripting Bridge?


Update:

If I replace iTunesTrack with SBObject, this works. I have no idea why, though.


Update 2:

This problem is reproducible:

  1. Create new project
  2. Add Scripting Bridge Framework and the iTunes.h header file.
  3. Create new category of iTunesTrack with an arbitrary name
  4. Ensure that iTunesTrack+name.h imports iTunes.h
  5. Build

I found this page which describes using NSProxy and NSCache to store iTunesTrack objects. Would this be a better solution than trying to make a category?

Scripting Bridge is quite a mess.

The class iTunesTrack is actually called ITunesTrack under the hood.
I think they were not quite consistent with the leading lowercase i .

This is why they changed it after a while, but probably did not want to do it in the header file, to not change their API.

I don't think there is a way to fix this.

You can, however, just create the category on SBObject .

// The interface can be declared as iTunesTrack
@interface iTunesTrack (Additions)
...
@end

// The category MUST be implemented on SBObject
@implementation SBObject (Additions)
...
@end

Caution

Be aware that the category will be available on every SBObject , so make sure that all properties and methods have a unique interface.

You can't put a category on iTunesTrack (or ITunesTrack, or whatever it's called in your header) because that requires the class to exist at link time, and it doesn't: Scripting Bridge creates the target application's classes dynamically at runtime. (And they have technically arbitrary names, which is why -classForScriptingClass exists.) It's still possible to add a method to the generated class, but it means mucking about with the runtime, which is generally more trouble than it's worth. Just put your category method on SBObject and try to be careful.

NSAddict's answer pointed the way to do something I've always wanted: implementing debugQuickLookObject for SBObject types to make debugging easier. Here's the category that does iTunesTrack and iTunesArtwork.

//  SBObject+Extensions.h

@import ScriptingBridge;

@interface SBObject (Extensions)

- (id)debugQuickLookObject;

@end


//  SBObject+Extensions.m

#import "iTunes.h"

@implementation SBObject (Extensions)

- (id)debugQuickLookObject
{
    NSString *className = self.className;

    if ([className isEqualToString:@"ITunesTrack"])
    {
        return [self handleITunesTrack];
    }
    else if ([className isEqualToString:@"ITunesArtwork"])
    {
        return [self handleITunesArtwork];
    }

    return [self description];
}

- (NSString*)handleITunesTrack
{
    iTunesTrack *track = (iTunesTrack *)self;

    NSMutableString *s = [NSMutableString new];

    [s appendFormat:@"Title:    %@\n", track.name];
    [s appendFormat:@"Artist:   %@\n", track.artist];
    [s appendFormat:@"Album:    %@\n", track.album];
    [s appendFormat:@"Duration: %f seconds\n", track.duration];

    return s;
}

- (NSImage*)handleITunesArtwork
{
    iTunesArtwork *artwork = (iTunesArtwork *)self;

    NSData *data   = [artwork rawData];
    NSImage *image = [[NSImage alloc] initWithData:data];

    return image;
}

@end

You may find the answer in this discussion: linker command failed with exit code 1 (use -v to see invocation)

Also you can try to clean and then rebuild your project or go to Project -> Build Settings -> Valid Architectures and check whether there's all correct. Some of these advises may help you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM