我最初是一名Java程序员,现在使用Objective-C。我想创建一个抽象类,但这在Objective-C中似乎不可能。这可能吗?
如果不是,在Objective-C中,我能有多接近抽象类?
我最初是一名Java程序员,现在使用Objective-C。我想创建一个抽象类,但这在Objective-C中似乎不可能。这可能吗?
如果不是,在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
其他回答
通过应用@dotToString的评论,稍微改变一下@redfood的建议,你实际上得到了Instagram的IGListKit所采用的解决方案。
为所有在基(抽象)类中定义没有意义的方法创建一个协议,即它们需要在子类中特定的实现。 创建一个不实现此协议的基(抽象)类。您可以向该类添加任何其他具有公共实现的方法。 在项目中的任何地方,如果必须通过某种方法输入或输出AbstractClass的子对象,请将其键入为AbstractClass<Protocol>。
因为AbstractClass没有实现Protocol,所以拥有AbstractClass<Protocol>实例的唯一方法是子类化。由于AbstractClass不能单独在项目中的任何地方使用,因此它变得抽象。
当然,这并不能阻止不明智的开发人员添加简单地引用AbstractClass的新方法,这最终将允许抽象类的实例(不再是)。
真实世界的例子:IGListKit有一个基类IGListSectionController,它不实现协议IGListSectionType,但是每个方法都需要这个类的实例,实际上要求类型IGListSectionController<IGListSectionType>。因此,在框架中没有办法使用IGListSectionController类型的对象来做任何有用的事情。
Cocoa不提供任何所谓抽象的东西。我们可以创建一个只在运行时检查的类抽象,而在编译时不检查。
与其尝试创建抽象基类,不如考虑使用协议(类似于Java接口)。这允许您定义一组方法,然后接受符合协议的所有对象并实现这些方法。例如,我可以定义一个操作协议,然后有一个这样的函数:
- (void)performOperation:(id<Operation>)op
{
// do something with operation
}
其中op可以是任何实现Operation协议的对象。
如果您需要抽象基类做的不仅仅是定义方法,那么您可以创建一个常规的Objective-C类并防止它被实例化。只需重写- (id)init函数,并使其返回nil或assert(false)。这不是一个非常干净的解决方案,但由于Objective-C是完全动态的,所以实际上没有与抽象基类直接等价的东西。
只是重复了上面@Barry Wark的回答(并更新到iOS 4.3),并把这个留给我自己参考:
#define mustOverride() @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"%s must be overridden in a subclass/category", __PRETTY_FUNCTION__] userInfo:nil]
#define methodNotImplemented() mustOverride()
在你的方法中你可以使用这个
- (void) someMethod {
mustOverride(); // or methodNotImplemented(), same thing
}
注意:不确定是否使宏看起来像一个C函数是一个好主意,但我会保持它直到学校相反。我认为使用NSInvalidArgumentException(而不是NSInternalInconsistencyException)更正确,因为这是运行时系统在响应doesNotRecognizeSelector被调用时抛出的(参见NSObject文档)。
在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");
就像我说的,这不是真正的编译器保护,但它和你在不支持抽象方法的语言中得到的一样好。