/*
• 存储属性的初始赋值
• 自定义构造过程
• 默认构造器
• 值类型的构造器代理
• 类的继承和构造过程
• 可失败构造器
• 必要构造器
• 通过闭包或函数设置属性的默认值
*/
//存储属性的初始赋值
//类和结构体在创建实例时,必须为所有存储型属性设置合适的初始值。存储型属性的值不能处于一个未知的状态。
//构造器:在创建某个特定类型的新实例时被调用,它的最简形式类似于一个不带任何参数的实例方法,以关键字 init 命名:
// init() {
//执行构造过程
// }
struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
var f = Fahrenheit()
print("The default temperature is \(f.temperature) ")
//默认属性值:在构造器中为存储型属性设置初始值。同样,你也可以在属性声明时为其设置默认值
//你可以使用更简单的方式在定义结构体 Fahrenheit 时为属性 temperature 设置默认值:
struct Fahrenheit2 {
var temperature = 32.0
}
var f2 = Fahrenheit2()
print("The default temperature is \(f2.temperature) ")
//自定义构造过程
//构造参数:
struct Celsius {
var temperatureInCelsius:Double
init(fromFahrenheit fahrenheit:Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
}
let boilingpointOfWater = Celsius(fromFahrenheit: 212.0)
let freezingPointWater = Celsius(fromKelvin: 273.15)
//参数的内部名称和外部名称
struct Color {
let red, green, blue:Double
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
init(white: Double) {
red = white
green = white
blue = white
}
}
//两种构造器都能用于创建一个新的 Color 实例,你需要为构造器每个外部参数传值:
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
let halfGray = Color(white: 0.5)
//注意,如果不通过外部参数名字传值,你是没法调用这个构造器的。只要构造器定义了某个外部参数名,你就必须使用它,忽略它将导致编译错误:
// let veryGreen = Color(0.0,1.0,0.0)
//不带外部名的构造器参数
//如果你不希望为构造器的某个参数提供外部名字,你可以使用下划线( _ )来显式描述它的外部名,以此重写上面 所说的默认行为。
struct Celsius2 {
var temperatureInCelsius:Double
init(fromFahrenheit fahrenheit:Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
init(_ celsius: Double) {
temperatureInCelsius = celsius
}
}
let bodyTemperature = Celsius2(37.0)
//可选属性类型
class SurveyQuestion {
var text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
print(text)
print(response ?? 0)
}
}
// 调查问题的答案在回答前是无法确定的,因此我们将属性 response 声明为 String? 类型,或者说是 可选字符串类 型 。当 SurveyQuestion 实例化时,它将自动赋值为 nil ,表明此字符串暂时还没有值。
let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?")
cheeseQuestion.response = "Yes ,I do like cheese"
cheeseQuestion.ask()
//构造过程中常量属性的修改
//以修改上面的 SurveyQuestion 示例,用常量属性替代变量属性 text ,表示问题内容 text 在 SurveyQuestio n 的实例被创建之后不会再被修改。尽管 text 属性现在是常量,我们仍然可以在类的构造器中设置它的值:
let beetsQuestion = SurveyQuestion(text: "How about beets?")
beetsQuestion.ask()
// 打印 "How about beets?"
beetsQuestion.response = "I also like beets. (But not with cheese.)"
//默认构造器
class ShoppingListItem {
var name:String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
// 由于ShoppingListItem类中的所有属性都有默认值,且它是没有父类的基类,它将自动获得一个可以为所有属性设置默认值的默认构造器(尽管代码中没有显式为name属性设置默认值,但由于name是可选字符串类型,它将默认设置为nil)。上面例子中使用默认构造器创造了一个ShoppingListItem类的实例(使用形式的构造器语法),并将其赋值给变量item。
//结构体的足以成员构造器
//除了上面提到的默认构造器,如果结构体没有提供自定义的构造器,它们将自动获得一个逐一成员构造器,即使结构体的存储型属性没有默认值。逐一成员构造器是用来初始化结构体新实例里成员属性的快捷方法
struct Size {
var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)
print(twoByTwo)
//值类型的构造代理
//对于值类型,你可以使用 self.init 在自定义的构造器中引用相同类型中的其它构造器。并且你只能在构造器内部调用 self.init 。如果你为某个值类型定义了一个自定义的构造器,你将无法访问到默认构造器(如果是结构体,还将无法访问逐 一成员构造器)。这种限制可以防止你为值类型增加了一个额外的且十分复杂的构造器之后,仍然有人错误的使用 自动生成的构造器
struct Point {
var x = 0.0 , y = 0.0
}
//你可以通过以下三种方式为 Rect 创建实例——使用被初始化为默认值的 origin 和 size 属性来初始化;提供指 定的 origin 和 size 实例来初始化;提供指定的 center 和 size 来初始化。在下面 Rect 结构体定义中,我们为 这三种方式提供了三个自定义的构造器:
struct Rect {
var origin = Point()
var size = Size()
init() {
}
init(origin: Point , size:Size) {
self.origin = origin
self.size = size
}
init(center: Point , size: Size) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
self.init(origin: Point(x: originX , y: originY), size:size)
}
}
// 第一个 Rect 构造器 init(),调用这个构造器将返回一个 Rect 实例,它的origin 和 size 属性都使用定义时的默认值 Point(x: 0.0, y: 0.0) 和 Size(width: 0.0, height: 0.0) :
let basicRect = Rect()
//第二个 Rect 构造器 init(origin:size:)在功能上跟结构体在没有自定义构造器时获得的逐一成员构造器是一 样的。这个构造器只是简单地将 origin 和 size 的参数值赋给对应的存储型属性:
let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))
//第三个 Rect 构造器 init(center:size:) 稍微复杂一点。它先通过 center 和 size 的值计算出 origin 的坐标,然 后再调用(或者说代理给) init(origin:size:) 构造器来将新的 origin 和 size 值赋值到对应的属性中:
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
size: Size(width: 3.0, height: 3.0))
//构造器 init(center:size:) 可以直接将 origin 和 size 的新值赋值到对应的属性中。然而,利用恰好提供了相关 功能的现有构造器会更为方便,构造器 init(center:size:) 的意图也会更加清晰。
//类的继承和构造过程-类里面的所有存储型属性——包括所有继承自父类的属性——都必须在构造过程中设置初始值。
//指定构造器和遍历构造器
//指定构造器是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。每一个类都必须拥有至少一个指定构造器,在某些情况下,许多类通过继承了父类中的指定构造器而满足了这个 条件
//便利构造器是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。
//指定构造器和遍历构造器的语法
//指定构造器
//init(parameters) {
// statements
//}
//遍历构造器,在 init 关键字之前放置 convenience 关键字
//convenience init(parameters) {
// statements
//}
//构造器的代理规则
//规则 1 指定构造器必须调用其直接父类的的指定构造器。
//规则 2 便利构造器必须调用同类中定义的其它构造器。
//规则 3 便利构造器必须最终导致一个指定构造器被调用。
//一个更方便记忆的方法是:
//• 指定构造器必须总是向上代理
//• 便利构造器必须总是横向代理
//构造器的继承和重写
class Vehicle {
var numberOfWheels = 0
var description: String {
return "\(numberOfWheels) wheel(s)"
}
}
//Vehicle类只为存储型属性提供默认值,而不自定义构造器。因此,它会自动获得一个默认构造器,
let vehicle = Vehicle()
print("Vehicle: \(vehicle.description)")
//下面例子中定义了一个 Vehicle 的子类 Bicycle :
//子类 Bicycle 定义了一个自定义指定构造器 init() 。这个指定构造器和父类的指定构造器相匹配,所以 Bicycle 中的指定构造器需要带上 override 修饰符。
class Bicycle: Vehicle {
override init() {
super.init()
numberOfWheels = 2
}
}
let bicycle = Bicycle()
print("Bicycle: \(bicycle.description)")
// 指定构造器和便利构造器实践
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
let namedMeat = Food(name: "Bacon")
//Food 类中的构造器 init(name: String) 被定义为一个指定构造器,因为它能确保 Food 实例的所有存储型属性都 被初始化。 Food 类没有父类,所以 init(name: String) 构造器不需要调用 super.init() 来完成构造过程。
//Food 类同样提供了一个没有参数的便利构造器 init() 。这个 init() 构造器为新食物提供了一个默认的占位名 字,通过横向代理到指定构造器 init(name: String) 并给参数 name 传值 [Unnamed] 来实现:
let mysteryMeat = Food()
// mysteryMeat 的名字是 [Unnamed]
//类层级中的第二个类是 Food 的子类 RecipeIngredient 。 RecipeIngredient 类用来表示食谱中的一项原料。它引 入了 Int 类型的属性 quantity (以及从 Food 继承过来的 name 属性),并且定义了两个构造器来创建 RecipeIng redient 实例:
class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
self.quantity = quantity
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name, quantity: 1)
}
}
//RecipeIngredient 类拥有一个指定构造器 init(name: String, quantity: Int) ,它可以用来填充 RecipeIngredie nt 实例的所有属性值。这个构造器一开始先将传入的 quantity 参数赋值给 quantity 属性,这个属性也是唯一在RecipeIngredient 中新引入的属性。随后,构造器向上代理到父类 Food 的 init(name: String) 。
let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)
class ShoppingListItems: RecipeIngredient {
var purchased = false
var description: String {
var output = "\(quantity) x \(name)"
output += purchased ? " ?" : " ?"
return output
}
}
//可失败的构造器
//这里所指的“失败”是指,如给构造器传入无效的参数值,或缺少某种所需的外部资源,又或是不满足某种必要的条件等。
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty {
return nil
}
self.species = species
}
}
let someCreature = Animal(species: "Giraffe")
// someCreature 的类型是 Animal? 而不是 Animal
if let giraffe = someCreature {
print("An animal was initialized with a species of \(giraffe.species)")
}
// 打印 "An animal was initialized with a species of Giraffe"
//如果你给该可失败构造器传入一个空字符串作为其参数,则会导致构造失败:
let anonymousCreature = Animal(species: "")
// anonymousCreature 的类型是 Animal?, 而不是 Animal
if anonymousCreature == nil {
print("The anonymous creature could not be initialized")
}
// 打印 "The anonymous creature could not be initialized"
//枚举类型的可失败构造器
enum TemperatureUnit {
case Kelvin, Celsius, Fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .Kelvin
case "C":
self = .Celsius
case "F":
self = .Fahrenheit
default:
return nil }
}
}
//你可以利用该可失败构造器在三个枚举成员中获取一个相匹配的枚举成员,当参数的值不能与任何枚举成员相匹配时,则构造失败:
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
// 打印 "This is a defined temperature unit, so initialization succeeded."
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, so initialization failed.")
}
// 打印 "This is not a defined temperature unit, so initialization failed."
//带原始值的枚举类型的可失败构造器
//带原始值的枚举类型会自带一个可失败构造器 init?(rawValue:) ,该可失败构造器有一个名为 rawValue 的参 数,其类型和枚举类型的原始值类型一致,如果该参数的值能够和某个枚举成员的原始值匹配,则该构造器会构 造相应的枚举成员,否则构造失败。
enum TemperatureUnits: Character {
case Kelvin = "K", Celsius = "C", Fahrenheit = "F"
}
let fahrenheitUnits = TemperatureUnits(rawValue: "F")
if fahrenheitUnits != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
// 打印 "This is a defined temperature unit, so initialization succeeded."
let unknownUnits = TemperatureUnits(rawValue: "X")
if unknownUnits == nil {
print("This is not a defined temperature unit, so initialization failed.")
}
// 打印 "This is not a defined temperature unit, so initialization failed."
//构造失败的传递:类,结构体,枚举的可失败构造器可以横向代理到类型中的其他可失败构造器。类似的,子类的可失败构造器也能向上代理到父类的可失败构造器。
class Product {
let name: String
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
class CartItem: Product {
let quantity: Int
init?(name: String, quantity: Int) {
if quantity < 1 { return nil }
self.quantity = quantity
super.init(name: name)
}
}
//如果你通过传入一个非空字符串 name 以及一个值大于等于 1 的 quantity 来创建一个 CartItem 实例,那 么构造方法能够成功被执行:
if let twoSocks = CartItem(name: "sock", quantity: 2) {
print("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)")
}
//倘若你以一个值为 0 的 quantity 来创建一个 CartItem 实例,那么将导致 CartItem 构造器失败:
if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
print("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)")
} else {
print("Unable to initialize zero shirts")
}
//同样地,如果你尝试传入一个值为空字符串的 name 来创建一个 CartItem 实例,那么将导致父类 Product 的 构造过程失败:
if let oneUnnamed = CartItem(name: "", quantity: 1) {
print("Item: \(oneUnnamed.name), quantity: \(oneUnnamed.quantity)")
} else {
print("Unable to initialize one unnamed product")
}
// 打印 "Unable to initialize one unnamed product”
//重写一个可失败构造器
//如同其它的构造器,你可以在子类中重写父类的可失败构造器。或者你也可以用子类的非可失败构造器重写一个父类的可失败构造器。这使你可以定义一个不会构造失败的子类,即使父类的构造器允许构造失败。
//注意,当你用子类的非可失败构造器重写父类的可失败构造器时,向上代理到父类的可失败构造器的唯一方式是对父类的可失败构造器的返回值进行强制解包。
class Document {
var name: String?
// 该构造器创建了一个 name 属性的值为 nil 的 document 实例
init() {}
// 该构造器创建了一个 name 属性的值为非空字符串的 document 实例
init?(name: String) {
self.name = name
if name.isEmpty { return nil }
}
}
// 下面这个例子,定义了一个 Document 类的子类 AutomaticallyNamedDocument 。这个子类重写了父类的两个指定构 造器,确保了无论是使用 init() 构造器,还是使用 init(name:) 构造器并为参数传递空字符串,生成的实例中的name 属性总有初始 "[Untitled]" :
class AutomaticallyNamedDocument: Document {
override init() {
super.init()
self.name = "[Untitled]"
}
override init(name: String ) {
super.init()
if name.isEmpty {
self.name = "[Untitled]"
}else {
self.name = name
}
}
}
//AutomaticallyNamedDocument 用一个非可失败构造器 init(name:) 重写了父类的可失败构造器 init?(name:) 。因 为子类用另一种方式处理了空字符串的情况,所以不再需要一个可失败构造器,因此子类用一个非可失败构造器 代替了父类的可失败构造器。
// 你可以在子类的非可失败构造器中使用强制解包来调用父类的可失败构造器。比如,下面的 UntitledDocument 子 类的 name 属性的值总是 "[Untitled]" ,它在构造过程中使用了父类的可失败构造器 init?(name:) :
class UntitledDocument: Document {
override init() {
super.init(name: "[Untitled]")!
}
}
// 在这个例子中,如果在调用父类的可失败构造器 init?(name:) 时传入的是空字符串,那么强制解包操作会引发运 行时错误。不过,因为这里是通过非空的字符串常量来调用它,所以并不会发生运行时错误。
//可失败构造器 init!
//通常来说我们通过在 init 关键字后添加问号的方式( init? )来定义一个可失败构造器,但你也可以通过在init后面添加惊叹号的方式来定义一个可失败构造器( init! ),该可失败构造器将会构建一个对应类型的隐式解 包可选类型的对象。
// 你可以在 init? 中代理到 init! ,反之亦然。你也可以用 init? 重写 init! ,反之亦然。你还可以用 init 代理 到 init! ,不过,一旦 init! 构造失败,则会触发一个断言。
//必要构造器
// 在类的构造器前添加 required 修饰符表明所有该类的子类都必须实现该构造器:
class SomeClass {
required init(){
//构造器的实现代码
}
}
//在子类重写父类的必要构造器时,必须在子类的构造器前也添加 required 修饰符,表明该构造器要求也应用于继 承链后面的子类。在重写父类中必要的指定构造器时,不需要添加 override 修饰符:
class SomeSubclass: SomeClass {
required init() {
// 构造器的实现代码
}
}
//通过闭包或函数设置属性的默认值
//下面介绍了如何用闭包为属性提供默认值:
//class SomeClass {
//let someProperty: SomeType = {
//// 在这个闭包中给 someProperty 创建一个默认值
//// someValue 必须和 SomeType 类型相同
// return someValue
//}()
//}
//注意闭包结尾的大括号后面接了一对空的小括号。这用来告诉 Swift 立即执行此闭包。如果你忽略了这对括 号,相当于将闭包本身作为值赋值给了属性,而不是将闭包的返回值赋值给属性。
struct Checkerboard {
let boardClolrs:[Bool] = {
var temporaryBoard = [Bool]()
var isBlack = false
for i in 1...8 {
for j in 1...8 {
temporaryBoard.append(isBlack)
isBlack = !isBlack
}
isBlack = !isBlack
}
return temporaryBoard
}()
func squareisBlackAtRow(row : NSInteger , colum: NSInteger) -> Bool {
return boardClolrs[(row * 8) + colum]
}
}
let board = Checkerboard()
print(board.squareisBlackAtRow(row: 0, colum: 1))
// 打印 "true"
print(board.squareisBlackAtRow(row: 7, colum: 7))
// 打印 "false"
swift-构造函数
最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
- 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
- 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
- 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
推荐阅读更多精彩内容
- [转]Swift构造函数和便利构造函数 构造函数基础 构造函数是一种特殊的函数,主要用来在创建对象时初始化对象,为...
- 一、oc中一般都是if (self = [super init]) return self; 都是先实例化父类,然...