在 Objective-C 中创建抽象类
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1034373/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Creating an abstract class in Objective-C
提问by Jonathan Arbogast
I'm originally a Java programmer who now works with Objective-C. I'd like to create an abstract class, but that doesn't appear to be possible in Objective-C. Is this possible?
我最初是一名 Java 程序员,现在使用 Objective-C。我想创建一个抽象类,但这在 Objective-C 中似乎是不可能的。这可能吗?
If not, how close to an abstract class can I get in Objective-C?
如果没有,我可以在 Objective-C 中获得多接近抽象类?
回答by Barry Wark
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:
通常,Objective-C 类只是按照约定是抽象的——如果作者将一个类记录为抽象类,那么不要在没有子类化的情况下使用它。但是,没有编译时强制措施可以阻止抽象类的实例化。事实上,没有什么可以阻止用户通过类别(即在运行时)提供抽象方法的实现。您可以通过在抽象类中的这些方法实现中引发异常来强制用户至少覆盖某些方法:
[NSException raise:NSInternalInconsistencyException
format:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)];
If your method returns a value, it's a bit easier to use
如果您的方法返回一个值,则使用起来更容易一些
@throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)]
userInfo:nil];
as then you don't need to add a return statement from the method.
那样你就不需要从方法中添加 return 语句。
If the abstract class is really an interface (i.e. has no concrete method implementations), using an Objective-C protocol is the more appropriate option.
如果抽象类确实是一个接口(即没有具体的方法实现),则使用 Objective-C 协议是更合适的选择。
回答by Grouchal
No, there is no way to create an abstract class in Objective-C.
不,没有办法在 Objective-C 中创建抽象类。
You can mock an abstract class - by making the methods/ selectors call doesNotRecognizeSelector: and therefore raise an exception making the class unusable.
您可以模拟抽象类 - 通过使方法/选择器调用 dosNotRecognizeSelector: 并因此引发异常使类无法使用。
For example:
例如:
- (id)someMethod:(SomeObject*)blah
{
[self doesNotRecognizeSelector:_cmd];
return nil;
}
You can also do this for init.
您也可以为 init 执行此操作。
回答by Dan Rosenstark
Just riffing on @Barry Wark's answer above (and updating for iOS 4.3) and leaving this for my own reference:
只是重复上面@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()
then in your methods you can use this
然后在你的方法中你可以使用它
- (void) someMethod {
mustOverride(); // or methodNotImplemented(), same thing
}
Notes:Not sure if making a macro look like a C function is a good idea or not, but I'll keep it until schooled to the contrary. I think it's more correct to use NSInvalidArgumentException(rather than NSInternalInconsistencyException) since that's what the runtime system throws in response to doesNotRecognizeSelectorbeing called (see NSObjectdocs).
注意:不确定让宏看起来像 C 函数是否是一个好主意,但我会保留它直到相反。我认为使用NSInvalidArgumentException(而不是NSInternalInconsistencyException)更正确,因为这是运行时系统响应doesNotRecognizeSelector被调用而抛出的内容(参见NSObject文档)。
回答by redfood
The solution I came up with is:
我想出的解决方案是:
- Create a protocol for everything you want in your "abstract" class
- Create a base class (or maybe call it abstract) that implements the protocol. For all the methods you want "abstract" implement them in the .m file, but not the .h file.
- Have your child class inherit from the base class AND implement the protocol.
- 在“抽象”类中为您想要的所有内容创建一个协议
- 创建一个实现协议的基类(或称其为抽象类)。对于您想要“抽象”的所有方法,在 .m 文件中实现它们,而不是在 .h 文件中。
- 让您的子类从基类继承并实现协议。
This way the compiler will give you a warning for any method in the protocol that isn't implemented by your child class.
这样,对于协议中未由您的子类实现的任何方法,编译器都会向您发出警告。
It's not as succinct as in Java, but you do get the desired compiler warning.
它不像在 Java 中那样简洁,但您确实会收到所需的编译器警告。
回答by redfood
From the Omni Group mailing list:
Objective-C doesn't have the abstract compiler construct like Java at this time.
目前,Objective-C 没有像 Java 那样的抽象编译器构造。
So all you do is define the abstract class as any other normal class and implement methods stubs for the abstract methods that either are empty or report non-support for selector. For example...
因此,您要做的就是将抽象类定义为任何其他普通类,并为空的或报告不支持选择器的抽象方法实现方法存根。例如...
- (id)someMethod:(SomeObject*)blah
{
[self doesNotRecognizeSelector:_cmd];
return nil;
}
I also do the following to prevent the initialization of the abstract class via the default initializer.
我还执行以下操作以防止通过默认初始化程序初始化抽象类。
- (id)init
{
[self doesNotRecognizeSelector:_cmd];
[self release];
return nil;
}
回答by Ben Gotow
Instead of trying to create an abstract base class, consider using a protocol (similar to a Java interface). This allows you to define a set of methods, and then accept all objects that conform to the protocol and implement the methods. For example, I can define an Operation protocol, and then have a function like this:
与其尝试创建抽象基类,不如考虑使用协议(类似于 Java 接口)。这允许您定义一组方法,然后接受所有符合协议的对象并实现这些方法。比如我可以定义一个Operation协议,然后有一个这样的函数:
- (void)performOperation:(id<Operation>)op
{
// do something with operation
}
Where op can be any object implementing the Operation protocol.
其中 op 可以是任何实现 Operation 协议的对象。
If you need your abstract base class to do more than simply define methods, you can create a regular Objective-C class and prevent it from being instantiated. Just override the - (id)init function and make it return nil or assert(false). It's not a very clean solution, but since Objective-C is fully dynamic, there's really no direct equivalent to an abstract base class.
如果您需要您的抽象基类做的不仅仅是定义方法,您可以创建一个常规的 Objective-C 类并防止它被实例化。只需覆盖 - (id)init 函数并使其返回 nil 或 assert(false)。这不是一个非常干净的解决方案,但由于 Objective-C 是完全动态的,因此实际上没有与抽象基类的直接等价物。
回答by danyowdee
This thread is kind of old, and most of what I want to share is already here.
这个帖子有点老了,我想分享的大部分内容已经在这里了。
However, my favorite method is not mentioned, and AFAIK there's no native support in the current Clang, so here I go…
但是,没有提到我最喜欢的方法,而且 AFAIK 在当前的 Clang 中没有本机支持,所以我开始......
First, and foremost (as others have pointed out already) abstract classes are something very uncommon in Objective-C — we usually use composition (sometimes through delegation) instead. This is probably the reason why such a feature doesn't already exist in the language/compiler — apart from @dynamicproperties, which IIRC have been added in ObjC 2.0 accompanying the introduction of CoreData.
首先,也是最重要的(正如其他人已经指出的)抽象类在 Objective-C 中非常罕见——我们通常使用组合(有时通过委托)来代替。这可能是语言/编译器中不存在此类功能的原因——除了@dynamic属性之外,随着 CoreData 的引入,IIRC 已添加到 ObjC 2.0 中。
But given that (after careful assessment of your situation!) you have come to the conclusion that delegation (or composition in general) isn't well suited to solving your problem, here's how Ido it:
但是鉴于(在仔细评估您的情况之后!)您得出的结论是委托(或一般的组合)不太适合解决您的问题,以下是我的做法:
- Implement every abstract method in the base class.
- Make that implementation
[self doesNotRecognizeSelector:_cmd];… - …followed by
__builtin_unreachable();to silence the warning you'll get for non-void methods, telling you “control reached end of non-void function without a return”. - Either combine steps 2. and 3. in a macro, or annotate
-[NSObject doesNotRecognizeSelector:]using__attribute__((__noreturn__))in a category without implementationso as not to replace the original implementation of that method, and include the header for that category in your project's PCH.
- 在基类中实现每个抽象方法。
- 使该实施
[self doesNotRecognizeSelector:_cmd];... - ……接着是
__builtin_unreachable();消除非空方法的警告,告诉你“控制到达非空函数的末尾而没有返回”。 - 在宏中组合步骤 2. 和 3.,或者在没有实现的类别中注释
-[NSObject doesNotRecognizeSelector:]using以免替换该方法的原始实现,并在项目的 PCH 中包含该类别的标题。__attribute__((__noreturn__))
I personally prefer the macro version as that allows me to reduce the boilerplate as much as possible.
我个人更喜欢宏版本,因为它可以让我尽可能地减少样板文件。
Here it is:
这里是:
// Definition:
#define D12_ABSTRACT_METHOD {\
[self doesNotRecognizeSelector:_cmd]; \
__builtin_unreachable(); \
}
// Usage (assuming we were Apple, implementing the abstract base class NSString):
@implementation NSString
#pragma mark - Abstract Primitives
- (unichar)characterAtIndex:(NSUInteger)index D12_ABSTRACT_METHOD
- (NSUInteger)length D12_ABSTRACT_METHOD
- (void)getCharacters:(unichar *)buffer range:(NSRange)aRange D12_ABSTRACT_METHOD
#pragma mark - Concrete Methods
- (NSString *)substringWithRange:(NSRange)aRange
{
if (aRange.location + aRange.length >= [self length])
[NSException raise:NSInvalidArgumentException format:@"Range %@ exceeds the length of %@ (%lu)", NSStringFromRange(aRange), [super description], (unsigned long)[self length]];
unichar *buffer = (unichar *)malloc(aRange.length * sizeof(unichar));
[self getCharacters:buffer range:aRange];
return [[[NSString alloc] initWithCharactersNoCopy:buffer length:aRange.length freeWhenDone:YES] autorelease];
}
// and so forth…
@end
As you can see, the macro provides the full implementation of the abstract methods, reducing the necessary amount of boilerplate to an absolute minimum.
如您所见,宏提供了抽象方法的完整实现,将必要的样板数量减少到绝对最少。
An even better option would be to lobby theClang teamto providing a compiler attribute for this case, via feature requests. (Better, because this would also enable compile-time diagnostics for those scenarios where you subclass e.g. NSIncrementalStore.)
更好的选择是游说Clang 团队通过功能请求为这种情况提供编译器属性。(更好,因为这还可以为那些子类化的场景启用编译时诊断,例如 NSIncrementalStore。)
Why I Choose This Method
为什么我选择这种方法
- It get's the job done efficiently, and somewhat conveniently.
- It's fairly easy to understand. (Okay, that
__builtin_unreachable()may surprise people, but it's easy enough to understand, too.) - It cannot be stripped in release builds without generating other compiler warnings, or errors — unlike an approach that's based on one of the assertion macros.
- 它可以高效地完成工作,而且有点方便。
- 这很容易理解。(好吧,这
__builtin_unreachable()可能会让人们感到惊讶,但它也很容易理解。) - 在不生成其他编译器警告或错误的情况下,不能在发布版本中剥离它 - 与基于断言宏之一的方法不同。
That last point needs some explanation, I guess:
最后一点需要一些解释,我想:
Some (most?) people strip assertions in release builds. (I disagree with that habit, but that's another story…) Failing to implement a required method — however — is bad, terrible, wrong, and basically the end of the universefor your program. Your program cannot work correctly in this regard because it is undefined, and undefined behavior is the worst thing ever. Hence, being able to strip those diagnostics without generating new diagnostics would be completely unacceptable.
一些(大多数?)人在发布版本中剥离断言。(我不同意这种习惯,但那是另外一回事了……) 未能实现所需的方法——然而——是糟糕的、可怕的、错误的,并且基本上是你的程序的世界末日。您的程序在这方面无法正常工作,因为它是未定义的,而未定义的行为是有史以来最糟糕的事情。因此,能够在不生成新诊断的情况下剥离这些诊断是完全不可接受的。
It's bad enough that you cannot obtain proper compile-time diagnostics for such programmer errors, and have to resort to at-run-time discovery for these, but if you can plaster over it in release builds, why try having an abstract class in the first place?
对于此类程序员错误,您无法获得正确的编译时诊断,这已经够糟糕了,并且不得不求助于运行时发现这些错误,但是如果您可以在发布版本中覆盖它,为什么要尝试在第一名?
回答by Cameron Spickert
Using @propertyand @dynamiccould also work. If you declare a dynamic property and don't give a matching method implementation, everything will still compile without warnings, and you'll get an unrecognized selectorerror at runtime if you try to access it. This essentially the same thing as calling [self doesNotRecognizeSelector:_cmd], but with far less typing.
使用@property和@dynamic也可以工作。如果你声明了一个动态属性并且没有给出匹配的方法实现,那么一切仍然会在没有警告的情况下编译,unrecognized selector如果你尝试访问它,你会在运行时得到一个错误。这与 call 本质上是一样的[self doesNotRecognizeSelector:_cmd],但打字要少得多。
回答by Richard Stelling
In Xcode (using clang etc) I like to use __attribute__((unavailable(...)))to tag the abstract classes so you get an error/warning if you try and use it.
在 Xcode(使用 clang 等)中,我喜欢用来__attribute__((unavailable(...)))标记抽象类,因此如果您尝试使用它,则会收到错误/警告。
It provides some protection against accidentally using the method.
它提供了一些防止意外使用该方法的保护。
Example
例子
In the base class @interfacetag the "abstract" methods:
在基类@interface标签中的“抽象”方法:
- (void)myAbstractMethod:(id)param1 __attribute__((unavailable("You should always override this")));
Taking this one-step further, I create a macro:
更进一步,我创建了一个宏:
#define UnavailableMacro(msg) __attribute__((unavailable(msg)))
This lets you do this:
这让您可以这样做:
- (void)myAbstractMethod:(id)param1 UnavailableMacro(@"You should always override this");
Like I said, this is not real compiler protection but it's about as good as your going to get in a language that doesn't support abstract methods.
就像我说的,这不是真正的编译器保护,但它与您使用不支持抽象方法的语言一样好。
回答by hadaytullah
The answer to the question is scattered around in the comments under the already given answers. So, I am just summarising and simplifying here.
问题的答案分散在已给出答案下的评论中。所以,我只是在这里总结和简化。
Option1: Protocols
选项 1:协议
If you want to create an abstract class with no implementation use 'Protocols'. The classes inheriting a protocol are obliged to implement the methods in the protocol.
如果您想创建一个没有实现的抽象类,请使用“协议”。继承协议的类有义务实现协议中的方法。
@protocol ProtocolName
// list of methods and properties
@end
Option2: Template Method Pattern
选项 2:模板方法模式
If you want to create an abstract class with partial implementation like "Template Method Pattern" then this is the solution. Objective-C - Template methods pattern?
如果您想创建一个具有部分实现的抽象类,例如“模板方法模式”,那么这就是解决方案。 Objective-C - 模板方法模式?

