Objective-C Redefine Class
I have some Code that I'd like to share between an iOS and an OSX project. Unfortunately, some classes, wh开发者_运维问答ile functionally relatively similar (and having greatly overlapping interface definitions), are parts of different hierarchies and frameworks, and have different names. UIKit/AppKit classes are a prime example.
One solution would be to use preprocessor directives, but that creates a lot of duplicate code:
#if TARGET_OS_IPHONE
- (UIImage *)getImage; //have to create an implementation for each
#else
- (NSImage *)getImage; //have to create an implementation for each
#endif;
I had thought about using reflection. In Objective-C, it's possible to dynamically get a Class object with the use of NSClassFromString(@"name")
. This is very nice, but if used often, creates ugly code. It also can't be used in a header file, because it's not constant, so this does not work:
//defined once per project
#if TARGET_OS_IPHONE
Class NUImage = NSClassFromString(@"UIImage");
#else
Class NUImage = NSClassFromString(@"NSImage");
#endif;
//usage
- (NUImage *)getImage; //does not compile
I want to be able to define (ideally at compile time) a Class Synonym, so that it can be used transparently in place of the specific implementation class. Is this possible? In essence, I want to avoid having to write a wrapper class (which would either just forward messages to the concrete implementation, thus be easy to write but take away code completion and checking, or be a lot of boilerplate code just to duplicate the interface of the class(es) it's wrapping).
Is it possible to store the Class as a constant?
You should try something like:
#if TARGET_OS_IPHONE
typedef UIImage NUIImage; // NUIImage is an alias for UIImage
#else
typedef NSImage NUIImage; // NUIImage is an alias for NSImage
#endif
And then use NUIImage everywhere you would have used either NSImage or UIImage. Typedef is inherited from C, so it's a compile cost only with no impact upon the runtime (so, e.g. NSClassFromString(@"NUIImage")
would return nil
all other thing being equal). It causes the compiler to consider the one thing to be an alias of the other. If you were adopting strict C conventions you'd probably call it NUIImage_t
.
The following is an edited version of my original answer, based on an original, mistaken reading that the question hinged on using a specific class that may or may not exist, with that fact not known until runtime. It's accurate for that sort of situation, but not especially relevant:
You don't need to find a solution that works in method definitions or in any of the other definitions normally used in a header file; you can just use the class name there. In Objective-C you always refer to objects by pointer and all pointers look the same to the hardware. The use of typed pointers in source code is therefore a bit of a fiction for the benefit of the programmer (including indirectly via relevant compiler warnings) with no effect on the generated binary.
As long as a definition like the following compiles, it doesn't matter whether neither SomeClass
nor SomeOtherClass
are available at runtime:
- (SomeClass *)someMethod:(SomeOtherClass *)argument;
You won't get an exception just for having declared the method.
The only place you really need to use NSClassFromString
is wherever you make an initial branch on whether or not a type of object is defined. For example, you can write the following class:
@interface SomeClassThatUsesSomeOtherClass
- (id)init;
@property (nonatomic, readonly) SomeOtherClass *otherClassInstance;
- (void)doSomeTask;
- (SomeOtherClassResult *)doSomeOtherTask;
@end
/* elsewhere */
@implementation SomeClassThatUsesSomeOtherClass
@synthesize otherClassInstance;
- (id)init
{
// blah blah blah, standard init stuff here
// switch is made implicitly here, because if we get nil back
// then we'll end up with nil in 'otherClassInstance' — we're
// considering the class not existing to be equivalent to any
// other issue that might cause an instance not to be created
otherClassInstance = [[NSStringFromClass(@"SomeClass") alloc] init];
if(!otherClassInstance)
{
[self release];
return nil;
}
// blah blah blah, rest of standard init
}
// you'll need a suitable dealloc, too
- (void)doSomeTask
{
[self.otherClassInstance doAThing];
[self.otherClassInstance doAnotherThing];
}
- (SomeOtherClassResult *)doSomeOtherTask
{
return [self.otherClassInstance doAThirdThing];
}
@end
And your code is completely safe, automatically working or not working depending on whether the class is available.
You could (this is how I was doing it back then) use the preprocessor
like this:
#define MY_CLASS_NAME @"MyClassName"
in separate .h file (which you could include in your .pch file) and then use it like this where you need
Class myClassInstance = NSClassFromString(MY_CLASS_NAME);
:)
精彩评论