在 Objective-C 中创建一个抽象类

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?

If not, how close to an abstract class can I get in Objective-C?

转载于:https://stackoverflow.com/questions/1034373/creating-an-abstract-class-in-objective-c

csdnceshi77
狐狸.fox Though Barry mentions it as an after thought (forgive me if I'm reading it wrong), I think you're looking for a Protocol in Objective C. See, for example, What is a Protocol?.
大约 6 年之前 回复
weixin_41568127
?yb? so have a look at the CocoaDev site which gives it a java comparison cocoadev.com/index.pl?AbstractSuperClass
10 年多之前 回复
csdnceshi62
csdnceshi62 Thanks for the info on the mentality of the Objective-C community as opposed to other languages. That really does resolve a number of related questions I had (like why no straightforward mechanism for private methods, etc).
11 年多之前 回复
csdnceshi74
7*4 The answers below are great. I find this the issue of abstract classes is tangentially related to private methods — both are methods for restricting what client code can do, and neither exist in Objective-C. I think it helps to understand that the mentality of the language itself is fundamentally different from Java. See my answer to: stackoverflow.com/questions/1020070/#1020330
11 年多之前 回复

20个回答

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)];

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.

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.

csdnceshi55
~Onlooker Use protocols (for completely abstract class) or template method pattern where abstract class has partial implementation/ flow logic as given here stackoverflow.com/questions/8146439/…. See my answer below.
5 年多之前 回复
csdnceshi60
℡Wang Yan This worked out for me since, IMHO, throwing an exception is more obvious to other developers that this is a desired behavior more so than doesNotRecognizeSelector.
6 年多之前 回复
weixin_41568134
MAO-EYE With a category method on NSException + (instancetype)exceptionForCallingAbstractMethod:(SEL)selector this works very nicely.
6 年多之前 回复
csdnceshi77
狐狸.fox To clarify: You can declare methods in a @protocol definition, but you cannot define the methods there.
大约 7 年之前 回复
csdnceshi52
妄徒之命 I think the most appropriate part of the answer was mentioning you could use @protocol instead to just define methods.
9 年多之前 回复

No, there is no way to create an abstract class in Objective-C.

You can mock an abstract class - by making the methods/ selectors call doesNotRecognizeSelector: and therefore raise an exception making the class unusable.

For example:

- (id)someMethod:(SomeObject*)blah
{
     [self doesNotRecognizeSelector:_cmd];
     return nil;
}

You can also do this for init.

csdnceshi73
喵-见缝插针 As you said: calling an abstract method is a programming error. It should be treated as such, instead of relying on runtime error reporting (NSException). This I think is the biggest issue for developers coming from other languages where abstract means "cannot instantiate an object of this type" in Obj-C it means "things will go wrong at runtime when you instantiate this class".
5 年多之前 回复
csdnceshi57
perhaps? You absolutely can create abstract classes in Objective-C, and it's quite common. There are a number of Apple framework classes that do this. Apple's abstract classes throw specific exceptions (NSInvalidArgumentException), often by calling NSInvalidAbstractInvocation(). Calling an abstract method is a programming error, which is why it throws an exception. Abstract factories are commonly implemented as class clusters.
大约 6 年之前 回复
weixin_41568196
撒拉嘿哟木头 I didn't downvote it, but the suggestion that you should cause an exception to be raised in the init method is the most likely reason for it. The most common format for a subclass will start it's own init method by calling self = [super init] - which will obediently throw an exception. This format works well for most methods, but I would never do it in anything where the subclass might call it's super implementation.
大约 6 年之前 回复
csdnceshi62
csdnceshi62 Why would you not want to just use a protocol in this instance? To me, this is nice to know for only method stubs, but not for a whole abstract class. The use case for this seems limited.
6 年多之前 回复
csdnceshi60
℡Wang Yan I did not downvote, but the NSObject reference suggests using this where you do NOT want to inherit a method, not to enforce overriding of a method. Although those may be the same thing, perhaps :)
9 年多之前 回复

Just riffing on @Barry Wark's answer above (and updating for iOS 4.3) and leaving this for my own reference:

#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 doesNotRecognizeSelector being called (see NSObject docs).

weixin_41568110
七度&光 please see details - stackoverflow.com/a/31775500/2012219
大约 5 年之前 回复
weixin_41568110
七度&光 if u add base class and then inherit this class for example 10 times and forgot to implement this in one of classes u will get message with name of Base class not inherited one like Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[BaseDynamicUIViewController localizeUI] must be overridden in a subclass/category' In case i proposed u also get HomeViewController - method not implemented where HomeViewController inherited from Base - this will give more information
大约 5 年之前 回复
weixin_41568126
乱世@小熊 this is already taken care of with the NSException throw. NSLog is another way to go, I guess...
大约 5 年之前 回复
weixin_41568110
七度&光 for more details - #define setMustOverride() NSLog(@"%@ - method not implemented", NSStringFromClass([self class])); mustOverride()
大约 5 年之前 回复
weixin_41568126
乱世@小熊 Thanks, yeah, I have to stop with NSLog though I can type %@ %@ %@ faster than anyone now. But I do use the VTPG stuff occasionally. vgable.com/blog/2010/08/19/…
大约 8 年之前 回复
csdnceshi52
妄徒之命 I don't think so. We use __PRETTY_FUNCTION__ all over the place, via a DLog(...) macro, as suggested here: stackoverflow.com/a/969291/38557.
大约 8 年之前 回复
csdnceshi52
妄徒之命 Great. Changed it a bit, though: #define mustOverride() @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"%s must be overridden in a subclass/category", __PRETTY_FUNCTION__] userInfo:nil].
大约 8 年之前 回复
weixin_41568126
乱世@小熊 Sure thing, @TomA, I hope it gives you ideas for other code that you can macro. My most-used macro is a simple reference to a singleton: the code says universe.thing but it expands to [Universe universe].thing. Big fun, saving thousands of letters of code...
9 年多之前 回复

Using @property and @dynamic could 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 selector error at runtime if you try to access it. This essentially the same thing as calling [self doesNotRecognizeSelector:_cmd], but with far less typing.

(more of a related suggestion)

I wanted to have a way of letting the programmer know "do not call from child" and to override completely (in my case still offer some default functionality on behalf of the parent when not extended):

typedef void override_void;
typedef id override_id;

@implementation myBaseClass

// some limited default behavior (undesired by subclasses)
- (override_void) doSomething;
- (override_id) makeSomeObject;

// some internally required default behavior
- (void) doesSomethingImportant;

@end

The advantage is that the programmer will SEE the "override" in the declaration and will know they should not be calling [super ..].

Granted, it is ugly having to define individual return types for this, but it serves as a good enough visual hint and you can easily not use the "override_" part in a subclass definition.

Of course a class can still have a default implementation when an extension is optional. But like the other answers say, implement a run-time exception when appropriate, like for abstract (virtual) classes.

It would be nice to have built in compiler hints like this one, even hints for when it is best to pre/post call the super's implement, instead of having to dig through comments/documentation or... assume.

example of the hint

Probably this kind of situations should only happen at development time, so this might work:

- (id)myMethodWithVar:(id)var {
   NSAssert(NO, @"You most override myMethodWithVar:");
   return nil;
}

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:

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

Where op can be any object implementing the Operation protocol.

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.

weixin_41568131
10.24 I need a base class to implement certain functionality that my subclasses all share (removing duplication), but I also need a protocol for other methods that the baseclass should not handle (the abstract part). So I need to use both, and thats where it can get tricky to make sure your subclasses are implementing themselves properly.
7 年多之前 回复
csdnceshi67
bug^君 Abstract class have no the same purpose as protocol/interface ...
8 年多之前 回复
weixin_41568184
叼花硬汉 abstract classes -- at least in Java -- are not just interfaces. They also define some (or most) behavior. This approach could be good in some cases, though.
大约 9 年之前 回复
csdnceshi65
larry*wei To me, this seems to be the appropriate way to go for cases where you would use abstract class, at least when it is meant to mean "interface" really (like in C++). Are there any hidden downsides to this approach?
9 年多之前 回复

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.

It provides some protection against accidentally using the method.

Example

In the base class @interface tag the "abstract" methods:

- (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.

csdnceshi72
谁还没个明天 It's the same as NS_UNAVAILABLE which will trigger an error every time you will try to call the method marked with such attribute. I don't see how it can be used on abstract class.
6 年多之前 回复
csdnceshi68
local-host Make sure you have an -init method in your subclass.
接近 8 年之前 回复
csdnceshi54
hurriedly% I couldn't get it. I applied your suggestion on my base class -init method and now Xcode does not allow me create an instance of inherited class and a compile time error occurs unavailable.... Would you please explain more?
接近 8 年之前 回复

From the Omni Group mailing list:

Objective-C doesn't have the abstract compiler construct like Java at this time.

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;
}
csdnceshi64
游.程 That's exactly what you want when the abstract class is not supposed to be initialised via init. Its subclasses presumably know what super method to call, but this stops careless invocation via "new" or "alloc/init".
接近 4 年之前 回复
weixin_41568127
?yb? I don't think this is a viable solution as subclasses may have perfectly legitimate calls to [super init].
8 年多之前 回复
csdnceshi63
elliott.david I'm pretty sure the whole point is to raise an exception as soon as possible. Ideally it should be at compile time, but since there is no way to do that, they settled for a run time exception. This is akin to an assertion failure, which should never be raised in production code in the first place. In fact, an assert(false) might actually be better since it is clearer that this code should never run. The user can't do anything to fix it, the developer must fix it. Thus raising an exception or assertion failure here sounds like a good idea.
10 年多之前 回复
weixin_41568174
from.. The doesNotRecognizeSelector approach prevents Apple's suggested self=[super init] pattern.
接近 11 年之前 回复
weixin_41568196
撒拉嘿哟木头 I hadn't thought of using -doesNotRecognizeSelector: and I kinda like this approach in some ways. Does anyone know of a way to make the compiler issue a warning for an "abstract" method created either this way or by raising an exception? That would be awesome...
11 年多之前 回复

Another alternative

Just check the class in the Abstract class and Assert or Exception, whatever you fancy.

@implementation Orange
- (instancetype)init
{
    self = [super init];
    NSAssert([self class] != [Orange class], @"This is an abstract class");
    if (self) {
    }
    return self;
}
@end

This removes the necessity to override init

csdnceshi72
谁还没个明天 Well this is just to catch programmers for using the class directly, which i think makes for good use of an assert.
6 年多之前 回复
csdnceshi61
derek5. Would it be efficient to just return nil? Or would that cause an exception/other error to be thrown?
6 年多之前 回复
共20条数据 1 尾页
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问