jjzjj

ios - UIBezierPath子类初始化器

coder 2023-09-08 原文

我正在尝试创建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:)。但是,为什么UIBezierPath的init(rect:)没有在我们的子类MyBezierPath中继承?根据初始化程序继承的一般规则,应该这样。

说明

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

现在,此方法(及其 sibling )展示了一些Swift无法很好处理的异常功能:
  • 它不仅仅是初始化程序的变体;它是一个纯粹的便利构造函数。 Objective-C向我们展示UIBezierPath没有相应的真实初始化器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))
    

    这不是很好,但是我认为比在解决方案中必须重新定义所有初始化程序要令人满意的多。最后,我实际上以一种压缩的方式做着与您正在做的事情完全相同的事情。但是总的来说,我更喜欢第一个解决方案:首先不要继承子类。
  • 关于ios - UIBezierPath子类初始化器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29867782/

    有关ios - UIBezierPath子类初始化器的更多相关文章

    1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

      我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

    2. ruby-on-rails - 未初始化的常量 Psych::Syck (NameError) - 2

      在我的gem中,我需要yaml并且在我的本地计算机上运行良好。但是在将我的gem推送到ruby​​gems.org之后,当我尝试使用我的gem时,我收到一条错误消息=>"uninitializedconstantPsych::Syck(NameError)"谁能帮我解决这个问题?附言RubyVersion=>ruby1.9.2,GemVersion=>1.6.2,Bundlerversion=>1.0.15 最佳答案 经过几个小时的研究,我发现=>“YAML使用未维护的Syck库,而Psych使用现代的LibYAML”因此,为了解决

    3. ruby-on-rails - 未在 Ruby 中初始化的对象 - 2

      我在Rails工作并有以下类(class):classPlayer当我运行时bundleexecrailsconsole然后尝试:a=Player.new("me",5.0,"UCLA")我回来了:=>#我不知道为什么Player对象不会在这里初始化。关于可能导致此问题的操作/解释的任何建议?谢谢,马里奥格 最佳答案 havenoideawhythePlayerobjectwouldn'tbeinitializedhere它没有初始化很简单,因为你还没有初始化它!您已经覆盖了ActiveRecord::Base初始化方法,但您没有调

    4. ruby - 如何验证 IO.copy_stream 是否成功 - 2

      这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

    5. ruby-on-rails - ActionController::RoutingError: 未初始化常量 Api::V1::ApiController - 2

      我有用于控制用户任务的Rails5API项目,我有以下错误,但并非总是针对相同的Controller和路由。ActionController::RoutingError:uninitializedconstantApi::V1::ApiController我向您描述了一些我的项目,以更详细地解释错误。应用结构路线scopemodule:'api'donamespace:v1do#=>Loginroutesscopemodule:'login'domatch'login',to:'sessions#login',as:'login',via::postend#=>Teamroutessc

    6. Ruby——嵌套类和子类是一回事吗? - 2

      下面例子中的Nested和Child有什么区别?是否只是同一事物的不同语法?classParentclassNested...endendclassChild 最佳答案 不,它们是不同的。嵌套:Computer之外的“Processor”类只能作为Computer::Processor访问。嵌套为内部类(namespace)提供上下文。对于ruby​​解释器Computer和Computer::Processor只是两个独立的类。classComputerclassProcessor#Tocreateanobjectforthisc

    7. Ruby 文件 IO 定界符? - 2

      我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

    8. ruby - 这两个 Ruby 类初始化定义有什么区别? - 2

      我正在阅读一本关于Ruby的书,作者在编写类初始化定义时使用的形式与他在本书前几节中使用的形式略有不同。它看起来像这样:classTicketattr_accessor:venue,:datedefinitialize(venue,date)self.venue=venueself.date=dateendend在本书的前几节中,它的定义如下:classTicketattr_accessor:venue,:datedefinitialize(venue,date)@venue=venue@date=dateendend在第一个示例中使用setter方法与在第二个示例中使用实例变量之间是

    9. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

      1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

    10. ruby - 为什么当我调用类的实例方法时,初始化不显示为方法? - 2

      我正在写一篇关于在Ruby中几乎一切都是对象的博客文章,我试图通过以下示例来展示这一点:classCoolBeansattr_accessor:beansdefinitialize@bean=[]enddefcount_beans@beans.countendend所以从类中我们可以看出它有4个方法(当然,除非我错了):它可以在创建新实例时初始化一个默认的空bean数组它可以计算它有多少个bean它可以读取它有多少个bean(通过attr_accessor)它可以向空数组写入(或添加)更多bean(也通过attr_accessor)但是,当我询问类本身它有哪些实例方法时,我没有看到默认

    随机推荐