我最初是一名Java程序员,现在使用Objective-C。我想创建一个抽象类,但这在Objective-C中似乎不可能。这可能吗?
如果不是,在Objective-C中,我能有多接近抽象类?
我最初是一名Java程序员,现在使用Objective-C。我想创建一个抽象类,但这在Objective-C中似乎不可能。这可能吗?
如果不是,在Objective-C中,我能有多接近抽象类?
当前回答
不,在Objective-C中无法创建抽象类。
你可以模拟一个抽象类——通过让methods/ selectors调用doesNotRecognizeSelector:从而引发一个异常,使该类不可用。
例如:
- (id)someMethod:(SomeObject*)blah
{
[self doesNotRecognizeSelector:_cmd];
return nil;
}
你也可以为init这样做。
其他回答
Typically, Objective-C class are abstract by convention only—if the author documents a class as abstract, just don't use it without subclassing it. There is no compile-time enforcement that prevents instantiation of an abstract class, however. In fact, there is nothing to stop a user from providing implementations of abstract methods via a category (i.e. at runtime). You can force a user to at least override certain methods by raising an exception in those methods implementation in your abstract class:
[NSException raise:NSInternalInconsistencyException
format:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)];
如果您的方法返回一个值,那么使用起来会更容易一些
@throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)]
userInfo:nil];
这样就不需要从方法中添加return语句了。
如果抽象类实际上是一个接口(即没有具体的方法实现),使用Objective-C协议是更合适的选择。
如果你习惯了编译器在其他语言中捕捉抽象实例化的冲突,那么Objective-C的行为是令人失望的。
作为一种后期绑定语言,Objective-C显然不能对一个类是否真的是抽象的做出静态决定(你可能在运行时添加函数……),但对于典型的用例来说,这似乎是一个缺点。我更喜欢编译器直接阻止抽象类的实例化,而不是在运行时抛出错误。
下面是我们使用的一个模式,使用一些技术来隐藏初始化式来获得这种类型的静态检查:
//
// Base.h
#define UNAVAILABLE __attribute__((unavailable("Default initializer not available.")));
@protocol MyProtocol <NSObject>
-(void) dependentFunction;
@end
@interface Base : NSObject {
@protected
__weak id<MyProtocol> _protocolHelper; // Weak to prevent retain cycles!
}
- (instancetype) init UNAVAILABLE; // Prevent the user from calling this
- (void) doStuffUsingDependentFunction;
@end
//
// Base.m
#import "Base.h"
// We know that Base has a hidden initializer method.
// Declare it here for readability.
@interface Base (Private)
- (instancetype)initFromDerived;
@end
@implementation Base
- (instancetype)initFromDerived {
// It is unlikely that this becomes incorrect, but assert
// just in case.
NSAssert(![self isMemberOfClass:[Base class]],
@"To be called only from derived classes!");
self = [super init];
return self;
}
- (void) doStuffUsingDependentFunction {
[_protocolHelper dependentFunction]; // Use it
}
@end
//
// Derived.h
#import "Base.h"
@interface Derived : Base
-(instancetype) initDerived; // We cannot use init here :(
@end
//
// Derived.m
#import "Derived.h"
// We know that Base has a hidden initializer method.
// Declare it here.
@interface Base (Private)
- (instancetype) initFromDerived;
@end
// Privately inherit protocol
@interface Derived () <MyProtocol>
@end
@implementation Derived
-(instancetype) initDerived {
self= [super initFromDerived];
if (self) {
self->_protocolHelper= self;
}
return self;
}
// Implement the missing function
-(void)dependentFunction {
}
@end
使用@property和@dynamic也可以。如果您声明了一个动态属性,但没有给出匹配的方法实现,那么所有内容仍然会在没有警告的情况下编译,并且如果您试图访问它,将在运行时得到一个无法识别的选择器错误。这本质上与调用[self doesNotRecognizeSelector:_cmd]是一样的,但是输入要少得多。
在Xcode中(使用clang等),我喜欢使用__attribute__((不可用(…)))来标记抽象类,所以如果你尝试使用它,你会得到一个错误/警告。
它提供了一些防止意外使用该方法的保护。
例子
在基类@interface标记中,"abstract"方法:
- (void)myAbstractMethod:(id)param1 __attribute__((unavailable("You should always override this")));
更进一步,我创建了一个宏:
#define UnavailableMacro(msg) __attribute__((unavailable(msg)))
这让你可以这样做:
- (void)myAbstractMethod:(id)param1 UnavailableMacro(@"You should always override this");
就像我说的,这不是真正的编译器保护,但它和你在不支持抽象方法的语言中得到的一样好。
问题的答案散落在已经给出答案的评论中。所以,我只是在这里总结和简化。
Option1:协议
如果你想创建一个没有实现的抽象类,请使用“协议”。继承协议的类必须实现协议中的方法。
@protocol ProtocolName
// list of methods and properties
@end
选项2:模板方法模式
如果你想创建一个带有部分实现的抽象类,如“Template Method Pattern”,那么这就是解决方案。 Objective-C -模板方法模式?