Conditional categories in Mountain Lion
Asked Answered
C

2

5

Mountain Lion introduced new APIs, some of which we had implemented as categories in our project.

For examples, we have a category NSColor+CGColorAdditions that implemented CGColor and colorWithCGColor: for NSColor. These methods have been added in Mountain Lion.

Ideally, we would like to use these categories if the client OS is older than Mountain Lion, and not use them if it's Mountain Lion. How can we do this? Or is there a better alternative?

Courtier answered 14/8, 2012 at 10:16 Comment(0)
M
7
NSColor *_NSColor_colorWithCGColor_(Class self, SEL cmd, CGColorRef cgColor)
{
    // make an NSColor outta `cgColor` and return it
    return nsColor;
}

// inside some initialization code

if ([[NSColor class] respondsToSelector:@selector(colorWithCGColor:)]) {
    // on ML, don't do anything
} else {
    // older system, add your own category
    class_addMethod(objc_getMetaClass("NSColor"), @selector(colorWithCGColor:), (IMP)_NSColor_colorWithCGColor_, "@@:@");
}
Moyna answered 14/8, 2012 at 10:26 Comment(4)
Thanks. Where would you recommend putting this initialization code? Can it be executed statically within -say- the category file?Courtier
@hgpc maybe among the very first lines of the application:didFinishLaunchingWithOptions: method.Moyna
Really? I'd put it in a +load method.Kathrinkathrine
@Freerunnering that's fine as well. Or in +initialize.Moyna
C
2

I thought it might also be helpful to post what I did based on H2CO3's answer.

NSColor+CGColorAdditions.m became:

static CGColorRef _NSColor_CGColor_(Class self, SEL cmd) {
    return nil; // TODO: Do something
}

static NSColor* _NSColor_colorWithCGColor_(Class self, SEL cmd, CGColorRef aColor) {
    return nil; // TODO: Do something    
}

__attribute__((constructor))
static void initialize_NSColor_CGColorAdditions() {
    if (![[NSColor class] respondsToSelector:@selector(colorWithCGColor:)]) {
        class_addMethod(objc_getMetaClass("NSColor"), @selector(colorWithCGColor:), (IMP)_NSColor_colorWithCGColor_, "@@:@");
    }
    if (![[NSColor class] instancesRespondToSelector:@selector(CGColor)]) {
        class_addMethod(objc_getClass("NSColor"), @selector(CGColor), (IMP)_NSColor_CGColor_, "@@:");
    }
}
Courtier answered 14/8, 2012 at 13:33 Comment(4)
I'm not so sure you want to use constructor for this, because it's not clear when it'll be called in relation to the runtime being set up. A +load category method is probably the appropriate place to do this.Brownlee
@W'rkncacnter As far as I know, constructor functions are called before main.Moyna
@H2CO3: Right -- my concern is that they're called before the runtime has been constructed, but I'm not exactly sure when that happens. It makes sense that it'd be when libobjc is loaded, so this is probably not a problem, but +load is explicitly for fiddling with classes before main.Brownlee
@H2CO3: But looking at the docs -- /me slaps forehead -- I see that it explicitly says that __attribute__((constructor))s are run after all +loads, so this definitely isn't a problem. Never mind!Brownlee

© 2022 - 2024 — McMap. All rights reserved.