如何在Swift中实现具有相同内容的两个没有代码重复的内容?

Mar*_*uro 49 ios swift

假设一个派生自UIView以下的类:

class MyView: UIView {
    var myImageView: UIImageView

    init(frame: CGRect) {
        super.init(frame: frame)
    }

    init(coder aDecoder: NSCoder!) {
        super.init(coder: aDecoder)
    }

    ...
Run Code Online (Sandbox Code Playgroud)

如果我想在两个初始化器中使用相同的代码,例如

self.myImageView = UIImageView(frame: CGRectZero)
self.myImageView.contentMode = UIViewContentMode.ScaleAspectFill
Run Code Online (Sandbox Code Playgroud)

并且在类实现中不重复该代码两次,我将如何构造init方法?

尝试的方法:

  • 创建了一个func commonInit()在调用之后调用的方法super.init- > Swift编译器myImageView在调用之前给出了一个关于未初始化变量的错误super.init
  • func commonInit()之前的调用super.init失败是显而易见的,编译器错误"'self'在super.init调用之前使用"

yli*_*x81 23

我们需要的是在调用任何超类的初始化器之前放置初始化代码的常见位置,所以我目前正在使用,如下面的代码所示.(它还涵盖了默认值之间相互依赖的情况,并使它们保持不变.)

import UIKit

class MyView: UIView {
        let value1: Int
        let value2: Int

        enum InitMethod {
                case coder(NSCoder)
                case frame(CGRect)
        }

        override convenience init(frame: CGRect) {
                self.init(.frame(frame))!
        }

        required convenience init?(coder aDecoder: NSCoder) {
                self.init(.coder(aDecoder))
        }

        private init?(_ initMethod: InitMethod) {
                value1 = 1
                value2 = value1 * 2 //interdependence among defaults

                switch initMethod {
                case let .coder(coder): super.init(coder: coder)
                case let .frame(frame): super.init(frame: frame)
                }
        }
}
Run Code Online (Sandbox Code Playgroud)


Mat*_*uch 21

我刚遇到同样的问题.

正如GoZoner所说,将变量标记为可选将起作用.这不是一种非常优雅的方式,因为每次要访问它时都需要打开值.

我将向Apple提交一个增强请求,也许我们可以得到类似于在每个init之前调用的"beforeInit"方法,我们可以在其中分配变量,这样我们就不必使用可选的变量.

在此之前,我将把所有赋值放入一个从专用初始化程序调用的commonInit方法中.例如:

class GradientView: UIView {
    var gradientLayer: CAGradientLayer?  // marked as optional, so it does not have to be assigned before super.init

    func commonInit() {
        gradientLayer = CAGradientLayer()
        gradientLayer!.frame = self.bounds
        // more setup 
    }

    init(coder aDecoder: NSCoder!)  {
        super.init(coder: aDecoder)
        commonInit()
    }

     init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        gradientLayer!.frame = self.bounds  // unwrap explicitly because the var is marked optional
    }
}
Run Code Online (Sandbox Code Playgroud)

感谢David,我再次看了一下这本书,我找到了一些可能对我们的重复数据删除工作有所帮助,而不必使用可选的变量hack.可以使用闭包来初始化变量.

使用闭包或函数设置默认属性值

如果存储属性的默认值需要某些自定义或设置,则可以使用闭包或全局函数为该属性提供自定义的默认值.每当初始化属性所属类型的新实例时,将调用闭包或函数,并将其返回值指定为属性的默认值.这些类型的闭包或函数通常会创建与属性相同类型的临时值,定制该值以表示所需的初始状态,然后返回该临时值以用作属性的默认值.

这是一个关于如何使用闭包来提供默认属性值的骨架轮廓:

class SomeClass {
let someProperty: SomeType = {
    // create a default value for someProperty inside this closure
    // someValue must be of the same type as SomeType
    return someValue
    }() 
}
Run Code Online (Sandbox Code Playgroud)

请注意,闭包的结束大括号后面是一对空括号.这告诉Swift立即执行关闭.如果省略这些括号,则尝试将闭包本身分配给属性,而不是闭包的返回值.

注意

如果使用闭包来初始化属性,请记住在执行闭包时尚未初始化实例的其余部分.这意味着您无法从闭包中访问任何其他属性值,即使这些属性具有默认值.您也不能使用隐式self属性,也不能调用任何实例的方法.

摘录自:Apple Inc."The Swift Programming Language."iBooks.https://itun.es/de/jEUH0.l

这是我从现在开始使用的方式,因为它没有规避不允许nil变量的有用功能.对于我的例子,它看起来像这样:

class GradientView: UIView {
    var gradientLayer: CAGradientLayer = {
        return CAGradientLayer()
    }()

    func commonInit() {
        gradientLayer.frame = self.bounds
        /* more setup */
    }

    init(coder aDecoder: NSCoder!)  {
        super.init(coder: aDecoder)
        commonInit()
    }

    init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 结合'commonInit()`的闭包正是我想要的,谢谢.或者只使用较短的形式:`var gradientLayer:CAGradientLayer = CAGradientLayer()`(或者甚至更短的`var gradientLayer = CAGradientLayer()`)如果你只想分配对象. (3认同)