ChatGPT解决这个技术问题 Extra ChatGPT

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

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

如果不是,我可以在 Objective-C 中获得多接近抽象类?

下面的答案很棒。我发现抽象类的问题与私有方法密切相关——两者都是限制客户端代码可以做什么的方法,在 Objective-C 中都不存在。我认为这有助于理解语言本身的思维方式与 Java 根本不同。请参阅我对 stackoverflow.com/questions/1020070/#1020330 的回答
感谢您提供有关 Objective-C 社区相对于其他语言的心态的信息。这确实解决了我遇到的许多相关问题(例如为什么没有直接的私有方法机制等)。
所以看看 CocoaDev 网站,它提供了一个 java 比较 cocoadev.com/index.pl?AbstractSuperClass
尽管 Barry 提到它是事后的想法(如果我读错了,请原谅我),但我认为您正在寻找 Objective C 中的 Protocol。例如,请参阅 What is a Protocol?

m
mblackwell8

通常,Objective-C 类只是按照约定是抽象的——如果作者将一个类记录为抽象类,则不要在没有继承它的情况下使用它。然而,没有编译时强制阻止抽象类的实例化。事实上,没有什么可以阻止用户通过类别(即在运行时)提供抽象方法的实现。您可以通过在抽象类的方法实现中引发异常来强制用户至少覆盖某些方法:

[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 协议是更合适的选择。


我认为答案中最合适的部分是提到您可以使用 @protocol 来定义方法。
澄清一下:您可以在 @protocol 定义中声明方法,但不能在那里定义方法。
使用 NSException + (instancetype)exceptionForCallingAbstractMethod:(SEL)selector 上的类别方法,这非常有效。
这对我来说很有效,因为恕我直言,抛出异常对于其他开发人员来说更明显,这是比 doesNotRecognizeSelector 更理想的行为。
使用协议(对于完全抽象类)或模板方法模式,其中抽象类具有部分实现/流程逻辑,如此处给出的 stackoverflow.com/questions/8146439/…。请看下面我的回答。
P
Peter Mortensen

不,没有办法在 Objective-C 中创建抽象类。

您可以模拟一个抽象类 - 通过使方法/选择器调用 doesNotRecognizeSelector: 并因此引发异常使该类不可用。

例如:

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

您也可以对 init 执行此操作。


@Chuck,我没有投反对票,但是 NSObject 参考建议在您不想继承方法的地方使用它,而不是强制覆盖方法。尽管这些可能是同一件事,但也许:)
为什么你不想在这种情况下只使用协议?对我来说,只知道方法存根很好,而不是整个抽象类。这个用例似乎有限。
我没有否决它,但建议您应该在 init 方法中引发异常是最可能的原因。子类最常见的格式将通过调用 self = [super init] 来启动它自己的 init 方法 - 这将乖乖地抛出异常。这种格式适用于大多数方法,但我永远不会在任何子类可能称之为超级实现的地方这样做。
您绝对可以在 Objective-C 中创建抽象类,这很常见。有许多 Apple 框架类可以做到这一点。 Apple 的抽象类通常通过调用 NSInvalidAbstractInvocation() 抛出特定异常 (NSInvalidArgumentException)。调用抽象方法是一个编程错误,这就是它抛出异常的原因。抽象工厂通常被实现为类集群。
@quellish:正如你所说:调用抽象方法是一个编程错误。应该这样对待它,而不是依赖运行时错误报告 (NSException)。我认为这对于来自其他语言的开发人员来说是最大的问题,其中抽象在 Obj-C 中意味着“无法实例化这种类型的对象”,这意味着“当你实例化这个类时,运行时会出错”。
D
Dan Rosenstark

只是重复上面@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 文档)。


当然,@TomA,我希望它能为您提供可以宏化的其他代码的想法。我最常用的宏是对单例的简单引用:代码显示为 universe.thing,但它扩展为 [Universe universe].thing。大乐趣,节省数千个代码字母...
伟大的。不过稍微改了一下:#define mustOverride() @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"%s must be overridden in a subclass/category", __PRETTY_FUNCTION__] userInfo:nil]
@Yar:我不这么认为。我们通过 DLog(...) 宏到处使用 __PRETTY_FUNCTION__,如下所示:stackoverflow.com/a/969291/38557
了解更多详情 - #define setMustOverride() NSLog(@"%@ - method not implemented", NSStringFromClass([self class])); mustOverride()
如果您添加基类然后继承此类,例如 10 次并忘记在其中一个类中实现此功能,您将收到带有未继承的基类名称的消息,例如 Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[BaseDynamicUIViewController localizeUI] must be overridden in a subclass/category' 如果我建议您也得到 HomeViewController - method not implemented,其中从 Base 继承的 HomeViewController - 这将提供更多信息
P
Peter Mortensen

我想出的解决方案是:

在“抽象”类中为您想要的所有内容创建一个协议 创建一个实现该协议的基类(或者称为抽象类)。对于您想要“抽象”的所有方法,在 .m 文件中实现它们,而不是在 .h 文件中。让您的子类从基类继承并实现协议。

这样,编译器将为您的子类未实现的协议中的任何方法发出警告。

它不像 Java 那样简洁,但您确实会收到所需的编译器警告。


+1这确实是最接近Java抽象类的解决方案。我自己也使用过这种方法,效果很好。甚至允许将协议命名为与基类相同的名称(就像 Apple 对 NSObject 所做的那样)。如果然后将协议和基类声明放在同一个头文件中,它几乎与抽象类没有区别。
啊,但是我的抽象类实现了协议的一部分,其余的由子类实现。
您可以只让子类实现协议,而将超类方法全部放在一起而不是空白。然后具有超类 类型的属性。此外,为了增加灵活性,您可以在协议中的方法前面加上 @optional。
这在 Xcode 5.0.2 中似乎不起作用;它只会为“抽象”类生成警告。不扩展“抽象”类会生成正确的编译器警告,但显然不会让您继承这些方法。
我更喜欢这个解决方案,但真的不喜欢它,它在项目中确实不是一个好的代码结构。 // 应该使用它作为子类的基类型。 typedef BaseClass BASECLASS;这只是一个星期的规则,我不喜欢它。
P
Peter Mortensen

Omni Group mailing list

Objective-C 目前没有像 Java 这样的抽象编译器结构。

因此,您所做的就是将抽象类定义为任何其他普通类,并为为空或报告不支持选择器的抽象方法实现方法存根。例如...

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

我还执行以下操作以防止通过默认初始化程序初始化抽象类。

- (id)init
{
     [self doesNotRecognizeSelector:_cmd];
     [self release];
     return nil;
}

我没有想过使用 -doesNotRecognizeSelector: 在某些方面我有点喜欢这种方法。有谁知道让编译器对以这种方式或通过引发异常创建的“抽象”方法发出警告的方法?那将是真棒...
doesNotRecognizeSelector 方法阻止了 Apple 建议的 self=[super init] 模式。
@david:我很确定重点是尽快提出异常。理想情况下,它应该在编译时进行,但由于无法做到这一点,因此他们选择了运行时异常。这类似于断言失败,一开始就不应该在生产代码中提出。事实上,assert(false) 实际上可能更好,因为更清楚的是,这段代码永远不应该运行。用户无法修复它,开发者必须修复它。因此,在这里提出异常或断言失败听起来是个好主意。
我不认为这是一个可行的解决方案,因为子类可能对 [super init] 有完全合法的调用。
@dlinsin:当抽象类不应该通过init初始化时,这正是您想要的。它的子类大概知道要调用什么超级方法,但这会阻止通过“new”或“alloc/init”进行的粗心调用。
B
Barry Wark

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

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

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

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


对我来说,这似乎是使用抽象类的适当方法,至少当它实际上意味着“接口”时(如在 C++ 中)。这种方法有什么隐藏的缺点吗?
@febeling,抽象类——至少在 Java 中——不仅仅是接口。它们还定义了一些(或大多数)行为。不过,这种方法在某些情况下可能会很好。
我需要一个基类来实现我的子类都共享的某些功能(删除重复),但我还需要一个协议用于基类不应处理的其他方法(抽象部分)。所以我需要同时使用这两者,这就是确保你的子类正确实现自己的地方。
C
Community

这个帖子有点老了,我想分享的大部分内容已经在这里了。

但是,没有提到我最喜欢的方法,而且 AFAIK 在当前的 Clang 中没有原生支持,所以我开始......

首先,最重要的是(正如其他人已经指出的那样)抽象类在 Objective-C 中非常少见——我们通常使用组合(有时通过委托)来代替。这可能是语言/编译器中尚不存在此类功能的原因 - 除了 @dynamic 属性之外,IIRC 已随着 CoreData 的引入而添加到 ObjC 2.0 中。

但是鉴于(在仔细评估了您的情况之后!)您得出的结论是委派(或一般的组合)不太适合解决您的问题,这就是我的做法:

实现基类中的每个抽象方法。使该实现 [self doesNotRecognizeSelector:_cmd];… …后跟 __builtin_unreachable();使非 void 方法的警告静音,告诉您“控制已到达非 void 函数的结尾而没有返回”。要么在宏中组合步骤 2. 和 3.,要么在没有实现的类别中使用 __attribute__((__noreturn__)) 注释 -[NSObject doesNotRecognizeSelector:],以免替换该方法的原始实现,并包含该方法的标头项目 PCH 中的类别。

我个人更喜欢宏版本,因为它可以让我尽可能地减少样板。

这里是:

// 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

如您所见,宏提供了抽象方法的完整实现,将所需的样板数量减少到绝对最小值。

一个更好的选择是 lobby the Clang team 通过功能请求为这种情况提供编译器属性。 (更好,因为这也将为您子类化的那些场景启用编译时诊断,例如 NSIncrementalStore。)

为什么我选择这种方法

它可以有效地完成工作,而且有些方便。这很容易理解。 (好吧,这 __builtin_unreachable() 可能会让人们感到惊讶,但它也很容易理解。)如果不生成其他编译器警告或错误,就无法在发布版本中剥离它——这与基于断言宏之一的方法不同。

最后一点需要一些解释,我猜:

一些(大多数?)人在发布版本中剥离断言。 (我不同意这种习惯,但那是另一回事了……) 未能实现所需的方法——然而——是不好的、可怕的、错误的,并且基本上对你的程序来说是宇宙的尽头。你的程序在这方面不能正常工作,因为它是未定义的,未定义的行为是最糟糕的事情。因此,能够在不生成新诊断的情况下剥离这些诊断将是完全不可接受的。

您无法为此类程序员错误获得适当的编译时诊断,这已经够糟糕了,并且必须在运行时发现这些错误,但是如果您可以在发布版本中对其进行修复,为什么要尝试在第一名?


这是我最喜欢的新解决方案 - __builtin_unreachable(); gem 完美地完成了这项工作。宏使其自我记录,并且行为与您在对象上调用缺少的方法时发生的情况相匹配。
C
Cameron Spickert

使用 @property@dynamic 也可以。如果您声明一个动态属性并且不提供匹配的方法实现,那么所有内容仍将在没有警告的情况下编译,并且如果您尝试访问它,您将在运行时收到 unrecognized selector 错误。这与调用 [self doesNotRecognizeSelector:_cmd] 基本相同,但输入要少得多。


C
Community

在 Xcode(使用 clang 等)中,我喜欢使用 __attribute__((unavailable(...))) 来标记抽象类,以便在尝试使用它时会收到错误/警告。

它提供了一些防止意外使用该方法的保护。

例子

在基类 @interface 标记中的“抽象”方法:

- (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");

就像我说的,这不是真正的编译器保护,但它与您使用不支持抽象方法的语言一样好。


我无法得到它。我在我的基类 -init 方法上应用了您的建议,现在 Xcode 不允许我创建继承类的实例,并且发生编译时错误不可用....请您解释一下吗?
确保您的子类中有一个 -init 方法。
它与 NS_UNAVAILABLE 相同,每次您尝试调用标有此类属性的方法时都会触发错误。我看不出它如何用于抽象类。
C
Community

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

选项 1:协议

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

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

选项2:模板方法模式

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


b
bigkm

另一种选择

只需检查抽象类中的类和断言或异常,无论你喜欢什么。

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

这消除了覆盖 init 的必要性


只返回 nil 会有效吗?还是会导致抛出异常/其他错误?
好吧,这只是为了让程序员直接使用该类,我认为这可以很好地使用断言。
I
Ivan Dossev

(更多相关建议)

我想有一种方法让程序员知道“不要从孩子那里打电话”并完全覆盖(在我的情况下,在未扩展时仍然代表父母提供一些默认功能):

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

优点是程序员会在声明中看到“覆盖”并且知道他们不应该调用 [super ..]

诚然,为此定义单独的返回类型是很难看的,但它作为一个足够好的视觉提示,您可以很容易地不在子类定义中使用“override_”部分。

当然,当扩展是可选的时,类仍然可以具有默认实现。但就像其他答案所说,在适当的时候实现一个运行时异常,比如抽象(虚拟)类。

最好内置像这样的编译器提示,甚至提示何时最好预先/后调用 super 的实现,而不必挖掘评论/文档或......假设。

https://i.stack.imgur.com/2K1wI.jpg


P
Peter Mortensen

如果您习惯于编译器在其他语言中捕获抽象实例化违规,那么 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

j
juanjo

可能这种情况应该只发生在开发时,所以这可能有效:

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

P
Peter Mortensen

您可以使用@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

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


P
Peter Mortensen

Cocoa 不提供任何称为抽象的内容。我们可以创建一个仅在运行时检查的类摘要,而在编译时不检查。


佚名

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

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

每当您在该类上调用 init 时,这将在编译时生成错误。然后我将类方法用于其他所有内容。

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


但是当我从该抽象类的派生类调用 [super init] 时出现错误。如何解决?
@SahilDoshi 我想这是使用这种方法的缺点。当我不想让一个类被实例化并且没有任何东西继承自该类时,我会使用它。
是的,我明白了。但是您的解决方案将有助于创建类似单例类的东西,我们不希望任何人调用 init。据我所知,在抽象类的情况下,某些类将继承它。否则抽象类的用途是什么。
G
Gobe

通过应用@dotToString 的评论稍微改变@redfood 的建议,您实际上拥有了Instagram 的IGListKit 采用的解决方案。

为在基(抽象)类中定义的所有没有意义的方法创建一个协议,即它们需要子类中的特定实现。创建一个不实现此协议的基(抽象)类。您可以向此类添加任何其他有意义的通用实现方法。在项目的任何地方,如果必须通过某种方法输入或输出来自 AbstractClass 的子项,请将其键入为 AbstractClass

因为 AbstractClass 没有实现 Protocol,所以拥有 AbstractClass<Protocol> 实例的唯一方法是通过子类化。由于不能在项目中的任何地方单独使用 AbstractClass,因此它变得抽象。

当然,这并不能阻止不知情的开发人员添加简单地引用 AbstractClass 的新方法,这最终会允许(不再是)抽象类的实例。

实际示例:IGListKit 有一个未实现协议 IGListSectionType 的基类 IGListSectionController,但是每个需要该类实例的方法实际上都要求类型 IGListSectionController<IGListSectionType>。因此,无法将 IGListSectionController 类型的对象用于其框架中的任何有用的东西。


P
Peter Mortensen

事实上,Objective-C 没有抽象类,但是你可以使用协议来达到同样的效果。这是示例:

自定义协议.h

#import <Foundation/Foundation.h>

@protocol CustomProtocol <NSObject>
@required
- (void)methodA;
@optional
- (void)methodB;
@end

测试协议.h

#import <Foundation/Foundation.h>
#import "CustomProtocol.h"

@interface TestProtocol : NSObject <CustomProtocol>

@end

测试协议.m

#import "TestProtocol.h"

@implementation TestProtocol

- (void)methodA
{
  NSLog(@"methodA...");
}

- (void)methodB
{
  NSLog(@"methodB...");
}
@end

S
Say2Manuj

创建抽象类的简单示例

// Declare a protocol
@protocol AbcProtocol <NSObject>

-(void)fnOne;
-(void)fnTwo;

@optional

-(void)fnThree;

@end

// Abstract class
@interface AbstractAbc : NSObject<AbcProtocol>

@end

@implementation AbstractAbc

-(id)init{
    self = [super init];
    if (self) {
    }
    return self;
}

-(void)fnOne{
// Code
}

-(void)fnTwo{
// Code
}

@end

// Implementation class
@interface ImpAbc : AbstractAbc

@end

@implementation ImpAbc

-(id)init{
    self = [super init];
    if (self) {
    }
    return self;
}

// You may override it    
-(void)fnOne{
// Code
}
// You may override it
-(void)fnTwo{
// Code
}

-(void)fnThree{
// Code
}

@end

u
user1709076

你不能只创建一个代表吗?

委托就像一个抽象基类,你说需要定义哪些函数,但实际上并没有定义它们。

然后,每当您实现委托(即抽象类)时,编译器都会警告您需要为其定义行为的可选函数和强制函数。

对我来说,这听起来像是一个抽象的基类。