我最初是一名Java程序员,现在使用Objective-C。我想创建一个抽象类,但这在Objective-C中似乎不可能。这可能吗?

如果不是,在Objective-C中,我能有多接近抽象类?


当前回答

问题的答案散落在已经给出答案的评论中。所以,我只是在这里总结和简化。

Option1:协议

如果你想创建一个没有实现的抽象类,请使用“协议”。继承协议的类必须实现协议中的方法。

@protocol ProtocolName
// list of methods and properties
@end

选项2:模板方法模式

如果你想创建一个带有部分实现的抽象类,如“Template Method Pattern”,那么这就是解决方案。 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

与其尝试创建抽象基类,不如考虑使用协议(类似于Java接口)。这允许您定义一组方法,然后接受符合协议的所有对象并实现这些方法。例如,我可以定义一个操作协议,然后有一个这样的函数:

- (void)performOperation:(id<Operation>)op
{
   // do something with operation
}

其中op可以是任何实现Operation协议的对象。

如果您需要抽象基类做的不仅仅是定义方法,那么您可以创建一个常规的Objective-C类并防止它被实例化。只需重写- (id)init函数,并使其返回nil或assert(false)。这不是一个非常干净的解决方案,但由于Objective-C是完全动态的,所以实际上没有与抽象基类直接等价的东西。

你可以使用@Yar提出的方法(做了一些修改):

#define mustOverride() @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"%s must be overridden in a subclass/category", __PRETTY_FUNCTION__] userInfo:nil]
#define setMustOverride() NSLog(@"%@ - method not implemented", NSStringFromClass([self class])); mustOverride()

在这里你会得到这样的消息:

<Date> ProjectName[7921:1967092] <Class where method not implemented> - method not implemented
<Date> ProjectName[7921:1967092] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[<Base class (if inherited or same if not> <Method name>] must be overridden in a subclass/category'

或断言:

NSAssert(![self respondsToSelector:@selector(<MethodName>)], @"Not implemented");

在这种情况下,你会得到:

<Date> ProjectName[7926:1967491] *** Assertion failure in -[<Class Name> <Method name>], /Users/kirill/Documents/Projects/root/<ProjectName> Services/Classes/ViewControllers/YourClass:53

您也可以使用协议和其他解决方案-但这是最简单的解决方案之一。

我通常只在我想要抽象的类中禁用init方法:

- (instancetype)__unavailable init; // This is an abstract class.

这将在编译时在该类上调用init时生成一个错误。然后我用类方法来处理其他事情。

Objective-C没有内置的方法来声明抽象类。

问题的答案散落在已经给出答案的评论中。所以,我只是在这里总结和简化。

Option1:协议

如果你想创建一个没有实现的抽象类,请使用“协议”。继承协议的类必须实现协议中的方法。

@protocol ProtocolName
// list of methods and properties
@end

选项2:模板方法模式

如果你想创建一个带有部分实现的抽象类,如“Template Method Pattern”,那么这就是解决方案。 Objective-C -模板方法模式?