我希望能够有两个负责响应选择器的类,具体取决于平台是iOS还是OSX。
但是,我希望代码只使用一个类,我想避免重复#ifdefs。
理想情况下,我希望有3个班级:
iOSSpecificClass
和OSXSpecificClass
都扩展了UniversalClass。
所有调用都将在UniversalClass中完成,该类负责调用iOSSpecificClass
和OSXSpecificClass
的相应方法。
我提出了两种解决方案:
@interface UniversalClass : NSObject
+ (void) universalMethod;
@end
@implementation UniversalClass
+(id)forwardingTargetForSelector:(SEL)aSelector {
#if TARGET_OS_IPHONE
return [iOSSpecificClass class];
#else
return [OSXSpecificClass class];
#endif
}
@end
这种方法的问题在于UniversalClass
承诺.h中可以提供或不能提供的内容。警告也告诉我们。格儿。警告。
第二种方法是这样的:
@implementation UniversalClass
+ (Class)correctClass {
Class aClass = Nil;
#if TARGET_OS_IPHONE
aClass = [iOSSpecificClass class];
#else
aClass = [OSXSpecificClass class];
#endif
return aClass;
}
+ (void)universalMethod {
Class masterClass = [UniversalClass correctClass];
[masterClass universalMethod];
}
@end
这种方法的问题在于我必须为我添加的每个方法执行更改,并且我觉得我有点重复自己而不需要。
在两种解决方案中我必须注意哪些边缘情况?有没有比那些更好的解决方案?
答案 0 :(得分:2)
一个选项是为两个目标创建一个公共头文件和两个不同的实现(一个用于OSX,另一个用于iOS),它们都导入并实现头方法。
这样的事情:
答案 1 :(得分:1)
另一种方法是检查你是否真的需要两个班级。一个@interface和两个@implementations(可能在单独的文件中)是我见过的模式。
类似的东西(来自CodeRunner,我在那里进行了测试):
#import <Foundation/Foundation.h>
// #define iPHONE 1
@interface MyClass : NSObject
- (NSString*) someString;
- (BOOL) aMethod: (NSString*) inString;
@end
// common implementations here
@interface MyClass (common)
- (NSString*) commonString;
@end
@implementation MyClass (common)
- (NSString*) commonString
{
return @"same";
}
@end
#ifdef iPHONE
// iPhone specific implementations
@implementation MyClass
- (BOOL) aMethod: (NSString*) inString
{
return [inString isEqualToString: @"iPhone Impl"];
}
- (NSString*) someString
{
return @"iPhone Impl";
}
@end
#else
@implementation MyClass
- (BOOL) aMethod: (NSString*) inString
{
return [inString isEqualToString: @"iPhone Impl"];
}
- (NSString*) someString
{
return @"OS X Impl";
}
@end
#endif
// test
int main(int argc, char *argv[]) {
@autoreleasepool {
MyClass * obj = [[MyClass alloc] init];
NSLog(@"is iPhone? %@", [obj aMethod: [obj someString]] ? @"YES" : @"NO");
NSLog( @"string: %@", [obj someString] );
}
}
显然,你可以通过拥有两个.m文件并在每个文件中放置一个实现(iPhone在一个,OS X在另一个中)来更优雅地做到这一点;如果你打算有两个共同的惯例,那就是三个。
无论如何,只是另一种获得相同/类似效果的方法 - 单一界面来实现不同的功能。
答案 2 :(得分:0)
你可以选择这样的东西:
@implementation UniversalClass
static Class class;
+ (void)load
{
class = [UniversalClass correctClass];
}
+ (Class)correctClass {
Class aClass = Nil;
#if TARGET_OS_IPHONE
aClass = [iOSSpecificClass class];
#else
aClass = [OSXSpecificClass class];
#endif
return aClass;
}
+ (void)universalMethod {
[class universalMethod];
}
这将通过实施相应的方法(无警告)来保留您对.h的承诺,并且只获得一次正确的类。
答案 3 :(得分:0)
如果忽略forwardingTargetForSelector:
版本特定情况的警告怎么样?这就像说“嘿,我知道我在做什么!”: - )
在#pragma
行周围添加类似@implementation
次调用的内容:
...
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wincomplete-implementation"
@implementation UniversalClass
#pragma clang diagnostic pop
...
答案 4 :(得分:0)
您提出的解决方案是类集群模式,这在Cocoa中很常见(例如,它用于NSArray,NSValue等)。类集群是从其构造函数返回私有子类的类,而不是所请求的类的实例。在这种情况下,您可以采用以下方式实现:
MyClass.h
@interface MyClass : NSObject
- (void)someMethod;
@end
MyClass.m
@implementation MyClass
+ (id)alloc
{
if (self == [MyClass class])
{
#if TARGET_OS_IPHONE
return [MyClass_iOS alloc];
#else
return [MyClass_Mac alloc];
#endif
}
else
{
return [super alloc];
}
}
- (void)someMethod
{
//abstract, will be overridden
}
@end
MyClass_iOS和MyClass_Mac将在单独的文件中声明,并在McClass.m文件中私下导入。
这看起来似乎是一个非常优雅的解决方案,但它并不适合这种情况。当你不知道在编译时想要哪个实现时,类集群很适合交换类在运行时 (很好的例子是支持不同的iOS版本,或者在iPad和iPhone上表现不同的通用应用程序) )但对于Mac / iOS,我们在编译时知道我们需要哪些代码,因此引入一个由3个独立类组成的集群是多余的。
此解决方案与https://stackoverflow.com/users/145108/dad或https://stackoverflow.com/users/3365314/miguel-ferreira建议的解决方案相比没有任何好处,因为我们仍然需要对导入代码进行分支:
#if TARGET_OS_IPHONE
#import "MyClass_iOS.h"
#else
#import "MyClass_Mac.h"
#endif
我们可以通过为MyClass_iOS和MyClass_Mac(这是Miguel的解决方案)提供单个标头,或者通过在同一个文件中使用这两个实现(这是Dad的解决方案)来解决这个问题,但是我们刚刚建立了一个层你已经拒绝的解决方案之一。
就个人而言,我只会使用一个带有三个明确描述部分的.m文件:
@interface MyClass
#pragma mark -
#pragma mark Common code
- (void)someMethod1
{
}
#pragma mark -
#pragma mark iOS code
#if TARGET_OS_IPHONE
- (void)someMethod2
{
}
#pragma mark -
#pragma mark Mac code
#else
- (void)someMethod2
{
}
#endif
@end
这样可以避免创建不必要的类,并且可以自由地为每个平台轻松拥有共享方法或单独的实现,而不会在接口中暴露任何类。
如果这两个平台的类肯定没有任何共同的代码,我可能会选择Miguel的解决方案,这非常干净。
我不接受“用户混淆”的解释。你基本上有这三个文件:
MyClass.h
MyClass_iOS.m
MyClass_Mac.m
我认为如果有人对这意味着什么感到困惑,他们就不应该在你的代码库上工作; - )
如果你想在两个平台之间继承共享代码,你也可以将它与类集群方法结合起来,在这种情况下你的MyClass.m文件将同时包含共享实现和私有接口:
@interface MyClass_Private : MyClass
- (void)somePlatformSpecificMethod;
@end
@implementation MyClass
+ (id)alloc
{
if (self == [MyClass class])
{
return [MyClass_Private alloc];
}
else
{
return [super alloc];
}
}
- (void)someSharedMethod
{
//concrete implementation
}
@end
您的项目结构看起来更像是这样:
MyClass.h
MyClass.m
MyClass_Private_iOS.m
MyClass_Private_Mac.m
希望有所帮助!