当前位置: 首页 > 面试题库 >

UIBezierPath子类初始化器

敖毅
2023-03-14
问题内容

我正在尝试创建UIBezierPath的子类,以添加一些对我有用的属性。

class MyUIBezierPath : UIBezierPath {
   var selectedForLazo : Bool! = false

   override init(){
       super.init()
   }

   /* Compile Error: Must call a designated initializer of the superclass 'UIBezierPath' */
   init(rect: CGRect){
       super.init(rect: rect)
   }

   /* Compile Error: Must call a designated initializer of the superclass 'UIBezierPath' */
   init(roundedRect: CGRect, cornerRadius: CGFloat) {
       super.init(roundedRect: roundedRect, cornerRadius: cornerRadius)
   }

   required init(coder aDecoder: NSCoder) {
       fatalError("init(coder:) has not been implemented")
   }
}

编辑:我需要这个,因为在我的代码中我写

var path = MyUIBezierPath(roundedRect: rect, cornerRadius: 7)

并导致编译错误:

“必须调用超类’UIBezierPath’的指定初始化程序”

我试图在子类中添加该初始化器,但似乎不起作用。

你能帮我吗?


问题答案:

注意 :此问题已在iOS 9中得以解决,在该版本中,API已被重写为init(rect:)存在,其他所有(方便初始化程序)也是如此。

问题

简而言之,您遇到的问题是以下代码无法编译:

    class MyBezierPath : UIBezierPath {}
    let b = MyBezierPath(rect:CGRectZero)

从Swift的角度来看,这似乎是错误的。该文档似乎说UIBezierPath有一个初始化程序init(rect:)。但是,为什么init(rect:)在我们的子类MyBezierPath中不继承UIBezierPath?根据初始化程序继承的一般规则,应该这样。

说明

UIBezierPath不适用于子类化。因此,它没有任何初始化程序-
除了init(),它是从NSObject继承的。在Swift中,UIBezierPath 看起来
好像具有初始化程序。但这是一个错误的表示。正如我们看到的Objective-
C标头所示,UIBezierPath实际上具有的是便捷构造函数,它们是类方法,例如:


+ (UIBezierPath *)bezierPathWithRect:(CGRect)rect;

现在,此方法(及其兄弟姐妹)展示了一些Swift不能很好处理的异常功能:

  • 它不仅是初始化程序的变体;它是一个 纯粹的 便利构造函数。Objective-C向我们展示UIBezierPath没有相应的true初始值设定项initWithRect:。在可可中,这是非常不寻常的情况。

  • 它返回UIBezierPath*,而不是instancetype。这意味着它不能被继承,因为它返回错误类型的实例。在子类MyBezierPath中,调用将bezierPathWithRect:产生UIBezierPath, 而不是 MyBezierPath。

斯威夫特严重地应付这种情况。一方面,它根据其通常的策略将类方法bezierPathWithRect:转换为表面上的初始化器init(rect:)。但是,另一方面,这不是“真正的”初始化程序,并且不能由子类继承。

因此,您被表观的初始化器所迷惑init(rect:),然后由于无法继承子类而无法在子类上调用它时感到惊讶和沮丧。

注意: 我并不是说Swift的行为不是错误;我认为这 一个错误(尽管我对将错误归咎于Swift还是UIBezierPath
API还是有些困惑)。Swift不应bezierPathWithRect:变成初始化程序,或者如果 确实
将其设为初始化程序,则应使该初始化程序可继承。无论哪种方式,它 都应该 是可继承的。但这不是,所以现在我们必须寻找一种解决方法。

解决方案

那你该怎么办?我有两种解决方案:

  • 不要继承。 首先,对UIBezierPath进行子类化是一个坏主意。它不是为这种事情而做的。取而代之的是子类的,做一个 包装 -一个类或结构,而不是具有的功能, 一个UIBezierPath,有它的功能 一个UIBezierPath。我们称之为MyBezierPathWrapper:
    struct MyBezierPathWrapper {
    var selectedForLazo : Bool = false
    var bezierPath : UIBezierPath!
    

    }

这只是将您的自定义属性和方法与普通的UIBezierPath结合在一起。然后可以分两步创建它,如下所示:

    var b = MyBezierPathWrapper()
b.bezierPath = UIBezierPath(rect:CGRectZero)

如果感觉不满意,可以通过添加采用UIBezierPath的初始化程序,使其一步创建。

    struct MyBezierPathWrapper {
    var selectedForLazo : Bool = false
    var bezierPath : UIBezierPath
    init(_ bezierPath:UIBezierPath) {
        self.bezierPath = bezierPath
    }
}

现在,您可以像这样创建它:

    var b = MyBezierPathWrapper(UIBezierPath(rect:CGRectZero))
  • 具有便利构造函数的子类。 如果您坚持子类化,即使UIBezierPath并非用于此类事情,也可以通过提供便捷构造函数来实现。之所以可行,是因为UIBezierPath唯一重要的是它的CGPath,因此您可以使此便捷构造函数成为一个 复制 构造函数,而只是从真实的UIBezierPath传递路径:
    class MyBezierPath : UIBezierPath {
    var selectedForLazo : Bool! = false
    convenience init(path:UIBezierPath) {
        self.init()
        self.CGPath = path.CGPath
    }
    

    }

现在,我们可以创建一种与以前的方法非常相似的方法:

    let b = MyBezierPath(path:UIBezierPath(rect:CGRectZero))

这不是很好,但是我认为比在解决方案中必须重新定义 所有
初始化程序要令人满意的多。最后,我实际上以一种更加压缩的方式做着与您正在做的事情完全相同的事情。但总的来说,我更喜欢第一个解决方案:首先不要继承子类。



 类似资料:
  • 我找不到任何关于这个具体案例的具体SO帖子,所以我想问一下我认为是/否的问题。 以下是JLS§12.4.2(Java SE 8),清单6-7: 我的问题是:这是否意味着子类的final static变量在超类的静态初始化之前初始化(假设final static作为其声明的一部分初始化)?

  • 问题内容: SuperClass object = new SubClass(); 为什么使用超类实例化上面的子类对象?因为我学会实例化对象的唯一方法是: 我正在学习Java。 问题答案: 您可能有一个仅接受实例的方法。由于 是 ,您可以使用的实例并将其视为。 使用接口时,会使用相同的行为: 这就是多态的美。它允许您更改类内部的实现,而无需破坏其余代码。

  • 我有一份抽象的课堂报告: 我需要强制所有子类填充此 surrogateId 属性。(如果子类未填充属性,则会出现编译错误) 我尝试使用 final 关键字来强制我在构造函数中引入此属性的值,但我必须手动生成构造函数而不是使用 Lombok,很多样板代码。有没有办法在不手动生成结构的情况下实现相同的期望。

  • 并且让我的所有子类在init时调用?这样做感觉不对,因为用foo完成的工作只会发生一次。

  • 问题内容: 作为学习练习,我尝试实现一个子类,该子类提供一个新的便捷初始化器,该初始化器采用一个数字并构造一个ShapeNode,该ShapeNode是一个数字宽度和高度的平方。 根据《雨燕书》: 规则1 如果您的子类没有定义任何指定的初始化器,它将自动继承其所有超类的指定初始化器。 规则二 如果您的子类提供了其所有超类指定初始化器的实现(通过按照规则1继承它们,或通过提供自定义实现作为其定义的一

  • 问题内容: 我最近刚与Python中的一个错误作斗争。那是那些愚蠢的新手错误之一,但是它让我思考了Python的机制(我是C ++的老程序员,是Python的新手)。我将列出错误的代码并解释如何解决该问题,然后我有两个问题。 场景:我有一个叫做A的类,它有一个字典数据成员,下面是其代码(当然这是简化的): 使用此代码的类为B类: 请注意,每次调用都会初始化类A的新“干净”实例,并在添加前后打印字典