有些类有一些众所周知的选择器,比如 UIView 层。如果有人继承 UIView 并声明他自己的“层”选择器用于不同的目的怎么办?然后是其他一些框架,如 UIKit 本身在 UIView 实例上调用层。这会在子类上调用“层”并导致崩溃?
4 回答
您永远不应覆盖现有方法并将其用于完全不同的目的。
当您有一个UIView
子类时,重写layer
以返回完全不同的东西,然后一个框架类(不知道您的重写目的)调用该layer
方法,取回它没有预料到的东西,事情会很快向南。
不,每个实例都知道它是什么类,如果您UIView
在覆盖子类中的某些内容之后创建了一个,那么UIView
它仍将使用该方法的版本。这是继承的基础——子类不应该影响父类。
@interface Stuper : NSObject
- (void)layer;
@end
@implementation Stuper
- (void)layer {
NSLog(@"superclass");
}
@end
@interface Stub : Stuper
@end
@implementation Stub
- (void)layer { // Override layer
NSLog(@"subclass");
}
@end
Stuper * p = [Stuper new];
Stub * b = [Stub new];
[p layer]; // Prints "superclass"
[b layer]; // Prints "subclass"
但是,如果您覆盖类别中的方法,则可能会发生这种情况,这就是您不应该这样做的原因。原类的所有实例都会使用新版本的方法,这会导致很大的问题,尤其是对于框架类。
@interface Stuper (BadIdea)
- (void)layer; // Clobber layer
@end
@implementation Stuper (BadIdea)
- (void)layer {
NSAssert(NO, @"Sucker!");
}
@end
Stuper * p = [Stuper new];
[p layer]; // Almost certainly doesn't do what the caller was expecting
当你想要子类化一个框架对象时,你首先应该看的是 Apple 提供的类引用。
在类参考中,有一个标题为子类化注释的部分,其中包含有关子类化的重要信息。本节包含有关哪些方法必须被覆盖、哪些方法应该被覆盖以及哪些方法不应该被覆盖的信息。
在这种情况下,layer
没有提到该方法,但是在“覆盖的方法”下它具有以下内容:
- layerClass - 仅当您希望视图使用不同的核心动画层作为其后备存储时才实施此方法。例如,如果您使用 OpenGL ES 进行绘图,您可能希望覆盖此方法并返回 CAEAGLLayer 类。
进一步研究一下,在layerClass
属性描述下,它有:
layerClass
返回用于为此类的实例创建层的类。
+ (Class)layerClass
返回值
用于创建视图的核心动画层的类。讨论
该方法默认返回 CALayer 类对象。子类可以重写此方法并根据需要返回不同的层类。例如,如果您使用 OpenGL ES 进行绘图,您将覆盖此方法并返回 CAEAGLLayer 类的类对象。此方法仅在创建视图的早期调用一次,以便创建相应的图层对象。
因此,这告诉我们您可以更改layer
预期返回的类,但它必须是“不同层类”(即 的子类CALayer
)。许多其他功能在布局、调整大小等时与图层交互,如果您不遵循他们的指导方针,您将遇到重大问题。
注意:
所有这些都适用于您的子类对象。当您在子类中进行更改时,它绝不会触及或应用于父类(UIView
在这种情况下),因此您不必担心破坏 UIView 对象,只需担心您自己的子类对象可能会导致崩溃和未定义的行为。
这听起来像是面向对象语言的基础。你选择一个与你的意图相似的类,然后创建它的一个子类并操纵它来做你想让它做的事情。大多数时候,尽管您需要告诉编译器您正在覆盖该类的该功能,以便它不会调用基类。