在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
值类型的构造器代理构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为构造器代理,它能减少多个构造器间的代码重复。 构造器代理的实现规则和形式在值类型和类类型中有所不同。值类型(结构体和枚举类型)不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给本身提供的其它构造器。类则不同,它可以继承自其它类,这意味着类有责任保证其所有继承的存储型属性在构造时也能正确的初始化。这些责任将在后续章节类的继承和构造过程中介绍。 对于值类型,你可以使用 如果你为某个值类型定义了一个定制的构造器,你将无法访问到默认构造器(如果是结构体,则无法访问逐一对象构造器)。这个限制可以防止你在为值类型定义了一个更复杂的,完成了重要准备构造器之后,别人还是错误的使用了那个自动生成的构造器。 注意:假如你想通过默认构造器、逐一对象构造器以及你自己定制的构造器为值类型创建实例,我们建议你将自己定制的构造器写到扩展( struct Size { var width = 0.0 var height = 0.0 } struct Point { var x = 0.0 var y = 0.0 } struct Rect { var size = Size() var origin = Point() init() {} init(origin: Point, size: Size){ self.size = size self.origin = origin } init(center: Point, size: Size){ let x = center.x - size.width / 2 let y = center.y - size.height / 2 self.init(origin:Point(x: x, y: y), size:size) } init(x: Double, y: Double, width: Double, height: Double){ self.init(origin: Point(x: x, y: y), size: Size(width: width, height: height)) } } let basicRect = Rect() let originRect = Rect(origin: Point(x: 0.0, y: 0.0), size: Size(width: 100.0, height: 100.0)) let centerRect = Rect(center: Point(x: 50.0, y: 50.0), size: Size(width: 100.0, height: 100.0)) let customRect = Rect(x: 25.0, y: 25.0, width: 50.0, height: 50.0) // 第三和第四个构造方式内部都是在构造内部代理给“init(origin: Point, size: Size)” 构造器来提供属性的值
指定构造器和便利构造器类里面的所有存储型属性--包括所有继承自父类的属性--都必须在构造过程中设置初始值。 Swift 提供了两种类型的类构造器来确保所有类实例中存储型属性都能获得初始值,它们分别是指定构造器和便利构造器。 指定构造器是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。 每一个类都必须拥有至少一个指定构造器。在某些情况下,许多类通过继承了父类中的指定构造器而满足了这个条件。 便利构造器是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入的实例。 你应当只在必要的时候为类提供便利构造器,比方说某种情况下通过使用便利构造器来快捷调用某个指定构造器,能够节省更多开发时间并让类的构造过程更清晰明了。 class SomeClass { var name = "" var discription: String? init(name: String) { // 这是自定构造器的写法 self.name = name } convenience init(discription: String) { // 这是便利构造器的写法 self.init(name: discription) self.discription = discription } }
类的构造器的代理规则Swift以三条规则来限制构造器之间的代理调用:
两段式构造过程Swift 中类的构造过程包含两个阶段。第一个阶段,每个存储型属性通过引入它们的类的构造器来设置初始值。当每一个存储型属性值被确定后,第二阶段开始,它给每个类一次机会在新实例准备使用之前进一步定制它们的存储型属性。 两段式构造过程的使用让构造过程更安全,同时在整个类层级结构中给予了每个类完全的灵活性。两段式构造过程可以防止属性值在初始化之前被访问;也可以防止属性被另外一个构造器意外地赋予不同的值。 Swift还会执行4中安全检查,以确保两段式构造过程能够顺利完成
以下是两段式构造过程中基于上述安全检查的构造流程展示:阶段1:
阶段2:
构造器的继承和重写跟 Objective-C 中的子类不同,Swift 中的子类不会默认继承父类的构造器。Swift 的这种机制可以防止一个父类的简单构造器被一个更专业的子类继承,并被错误的用来创建子类的实例。 假如你希望自定义的子类中能实现一个或多个跟父类相同的构造器,也许是为了完成一些定制的构造过程,你可以在你定制的子类中提供和重写与父类相同的构造器。 当你写一个父类中带有指定构造器的子类构造器时,你需要重写这个指定的构造器。因此,你必须在定义子类构造器时带上 相反地,如果你写了一个和父类便利构造器相匹配的子类构造器,子类都不能直接调用父类的便利构造器,每个规则都在上文构造器链有所描述。 class Vehicle { var numberOfWheels = 0 // 为存储属性设置了默认值,所以不必自定义构造器,Swift也会生成一个默认构造器"init()" var discription: String { return "\(numberOfWheels) wheel(s)" } } class Bicycle: Vehicle { // 子类自定义了一个构造器,这个构造器与父类的默认构造器相匹配,所以要加上关键字 "override" override init() { super.init() numberOfWheels = 2 } }
自动构造器的继承如上所述,子类不会默认继承父类的构造器。但是如果特定条件可以满足,父类构造器是可以被自动继承的。在实践中,这意味着对于许多常见场景你不必重写父类的构造器,并且在尽可能安全的情况下以最小的代价来继承父类的构造器。 假设要为子类中引入的任意新属性提供默认值,请遵守以下2个规则:
即使你在子类中添加了更多的便利构造器,这两条规则仍然适用。
演练class Food { var name: String // 指定构造器 init(name: String) { self.name = name } // 便利构造器 convenience init() { self.init(name: "[没有商品名]") } } class RecipeIngredient: Food { var quantity: Int // 指定构造器 init(name: String, quantity: Int) { // 这里遵循Swift的安全检查 // 1、确保子类的存储属性有值 self.quantity = quantity // 2、根据构造链向上调用父类的指定构造器,使父类的所有存储属性有值 super.init(name: name) } // 这里将父类的指定构造器重写为子类的便利构造器 override convenience init(name: String){ self.init(name: name, quantity: 1) } // 子类提供了父类所有的指定构造器,所以子类也默认继承了父类的所有便利构造器,这满足了规则2:(如果子类提供了所有父类指定构造器的实现--不管是通过规则1继承过来的,还是通过自定义实现的--它将自动继承所有父类的便利构造器。) } class ShoppingListItem: RecipeIngredient { var purchased = false // 是否购买 var discription: String { // 描述 var output = "\(quantity) x \(name.lowercaseString)" output += purchased ? "√" : "×" return output } // 注意:这个子类没有定义任何指定构造器,那么它将遵守规则1 自动继承所有父类的指定构造器 } let salt = RecipeIngredient(name: "盐", quantity: 2) let sugar = RecipeIngredient() print("sugar name \(sugar.name), quantity \(sugar.quantity)") // 打印出:"sugar name [没有商品名], quantity 1\n" // 为什么quantity属性会是1? 因为sugar初始化的时候调用的是继承来的便利构造器,在便利构造器中“(init())”父类设置它将构造过程代理给父类的指定构造器"init(name: String)",不过这个便利构造器被继承后不再调用父类的"init(name: String)",而是子类override的"(convenience init(name: String))" // 开始创建购物清单 var list = [ShoppingListItem(), ShoppingListItem(name: "Bacon"), ShoppingListItem(name: "Eggs", quantity: 5)] // 可以看到 ShoppingListItem这个子类继承了所有父类的构造方法 list[1].purchased = true for item in list { print(item.discription) } // 1 x [没有商品名]× // 1 x bacon√ // 5 x eggs×
|
请发表评论