Objective-C重新定义类

时间:2011-09-05 21:59:29

标签: objective-c class reflection constants

我有一些代码,我想在iOS和OSX项目之间共享。不幸的是,一些类虽然在功能上相对相似(并且具有极大重叠的接口定义),但它们是不同层次结构和框架的一部分,并且具有不同的名称。 UIKit / AppKit类是一个很好的例子。

一种解决方案是使用预处理程序指令,但这会产生大量重复的代码:

#if TARGET_OS_IPHONE
  - (UIImage *)getImage; //have to create an implementation for each
#else
  - (NSImage *)getImage; //have to create an implementation for each
#endif;

我曾想过使用反射。在Objective-C中,可以使用NSClassFromString(@"name")动态获取Class对象。这非常好,但如果经常使用,会产生丑陋的代码。它也不能在头文件中使用,因为它不是常量,所以这不起作用:

//defined once per project
#if TARGET_OS_IPHONE
  Class NUImage = NSClassFromString(@"UIImage");
#else
  Class NUImage = NSClassFromString(@"NSImage");
#endif;

//usage
- (NUImage *)getImage; //does not compile

我希望能够定义(理想情况下是在编译时)类同义词,以便可以透明地使用它来代替特定的实现类。这可能吗?从本质上讲,我想避免编写一个包装类(它只是将消息转发给具体的实现,因此很容易编写但是带走代码完成和检查,或者只是很多样板代码而已复制它所包装的类的接口。

是否可以将Class存储为常量?

2 个答案:

答案 0 :(得分:3)

你应该尝试类似的东西:

#if TARGET_OS_IPHONE
typedef UIImage NUIImage; // NUIImage is an alias for UIImage
#else
typedef NSImage NUIImage; // NUIImage is an alias for NSImage
#endif

然后在任何地方使用NUIImage,你会使用NSImage或UIImage。 Typedef继承自C,因此它只是一个编译成本,对运行时没有影响(因此,例如NSClassFromString(@"NUIImage")将返回nil所有其他相同的东西)。它使编译器将一件事视为另一件事的别名。如果您采用严格的C约定,则可能将其称为NUIImage_t


以下是我原始答案的编辑版本,基于原始的错误阅读,该问题取决于使用可能存在或可能不存在的特定类,该事实直到运行时才知道。它对于这种情况是准确的,但不是特别相关:

您不需要找到适用于方法定义或头文件中通常使用的任何其他定义的解决方案;你可以在那里使用类名。在Objective-C中,您总是通过指针引用对象,并且所有指针看起来都与硬件相同。因此,在源代码中使用类型指针对于程序员的好处(包括间接通过相关的编译器警告)来说有点虚构,对生成的二进制文件没有影响。

只要以下定义编译,在运行时SomeClassSomeOtherClass都不可用并不重要:

- (SomeClass *)someMethod:(SomeOtherClass *)argument;

只是因为声明了方法,你不会得到例外。

您真正需要使用NSClassFromString的唯一地方是无论您是否定义了某种类型的对象,都可以在哪里进行初始分支。例如,您可以编写以下类:

@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

您的代码完全安全,自动工作或不工作,具体取决于课程是否可用。

答案 1 :(得分:2)

你可以(这就是我当时的做法)使用这样的preprocessor

#define MY_CLASS_NAME @"MyClassName"

在单独的.h文件中(可以包含在.pch文件中)然后在需要的地方使用它

Class myClassInstance = NSClassFromString(MY_CLASS_NAME);

:)