Swift----方法 、 下标 、 继承 、 初始化 、 析构方法 、 可选链

时间:2023-03-10 01:04:28
Swift----方法 、 下标 、 继承 、 初始化 、 析构方法 、 可选链
  下标的使用

 1.1 问题

 下标可以定义在类、结构体和枚举中,可以认为是访问对象、集合或序列的快捷方式,不需要再调用实例的特定的赋值和访问方法。

 本案例定义一个Matrix结构体,用于呈现一个Double类型的二维矩阵,其结构体内部使用一个一维数组保存数据,并且定义一个下标用于判断是否会造成数组越界。

 1.2 方案

 首先定义一个Matrix结构体,该结构体有一个存储属性grid,是一个Double类型的结构体,用于存储矩阵的数据。

 Matrix结构体另有两个整型的常量存储属性rows和columns,分别表示矩阵的行数和列数。通过init初始化方法对属性grid、rows以及columns赋初始值。

 然后定义并实现下标运算,将传入的行号row和列号column转换为grid一维数组的下标,获取和设置对应的数据。

 最后定义一个方法indexIsValidForRow,用于判断行号和列号是否越界,通过断言进行判断。

 1.3 步骤

 实现此案例需要按照如下步骤进行。

 步骤一:定义Matrix结构体

 首先定义一个Matrix结构体,该结构体有一个存储属性grid,是一个Double类型的结构体,用于存储矩阵的数据。

 Matrix结构体另有两个整型的常量存储属性rows和columns,分别表示矩阵的行数和列数。通过init初始化方法对属性grid、rows以及columns赋初始值,代码如下所示:

 struct Metrix {
//保存数据的一维数组
var grid : [Double]
//矩阵的行列
let rows : Int, columns : Int
//初始化方法
init(rows:Int, columns:Int){
self.rows = rows
self.columns = columns
grid = [Double](count: rows*columns, repeatedValue: 0.0)
}
}
步骤二:实现下标运算 定义并实现下标运算,Marix的下标运算需要两个整型参数row和column,表示二维矩阵的下标,通过行号 row和列号column转换为grid一维数组的下标,获取和设置对应的数据,代码如下所示: struct Metrix {
//保存数据的一维数组
var grid : [Double]
//矩阵的行列
let rows : Int, columns : Int
//初始化方法
init(rows:Int, columns:Int){
self.rows = rows
self.columns = columns
grid = [Double](count: rows*columns, repeatedValue: 0.0)
}
subscript (row:Int,column:Int)->Double {
get {
return grid[row * columns + column]
}
set {
grid[row * columns + column] = newValue
}
}
}
步骤三:判断下标越界 在结构体中定义一个方法indexIsValidForRow,用于判断行号和列号是否越界,代码如下所示: //判断下标越界
funcindexIsValidForRow (row:Int,column:Int)->Bool {
let index = row * columns + column
return row >= && row <self.rows&& column >= && column <self.columns
}
在下标运算中通过断言进行判断,代码如下所示: subscript (row:Int,column:Int)->Double {
get {
assert(self.indexIsValidForRow(row, column: column), "下标越界")
return grid[row * columns + column]
}
set {
assert(self.indexIsValidForRow(row, column: column), "下标越界")
grid[row * columns + column] = newValue
}
}
然后创建一个Marix实例并进行赋值,运行结果如图-1所示: 图- 1.4 完整代码 本案例中,完整代码如下所示: importUIKit
struct Metrix {
//保存数据的一维数组
var grid : [Double]
//矩阵的行列
let rows : Int, columns : Int
//初始化方法
init(rows:Int, columns:Int){
self.rows = rows
self.columns = columns
grid = [Double](count: rows*columns, repeatedValue: 0.0)
}
//判断下标越界
funcindexIsValidForRow (row:Int,column:Int)->Bool {
let index = row * columns + column
return row >= && row <self.rows&& column >= && column <self.columns
}
subscript (row:Int,column:Int)->Double {
get {
assert(self.indexIsValidForRow(row, column: column), "下标越界")
return grid[row * columns + column]
}
set {
assert(self.indexIsValidForRow(row, column: column), "下标越界")
grid[row * columns + column] = newValue
}
}
}
var m = Metrix(rows: , columns: )
m[,] =
m[,] =
m[,] =
m[,] =
//下标越界
//m[0,4] = 500
m[,] =
m[,] =
隐藏 构造过程 2.1 问题 构造过程是为了使用某个类、结构体或枚举类型的实例而进行的准备过程,这个过程包含了为实例中的每个属性设置初始值和为其执行必要的准备和初始化任务。本案例分别演示值类型的构造过程和类的构造过程。 2.2 方案 类和结构体在实例创建时,必须为所有存储型属性设置合适的初始值,可以在在构造方法中为存储属性赋初始值,也可以在定义属性时为其设置默认值。构造方法以关键字init命名,最简单的形式是一个不带任何参数的实例方法。 在定义构造方法时也能提供参数,为构造过程中提供所需要的数据。 如果在定义构造方法时没有提供参数的外部名称,Swift会为每个构造方法的参数自动生成一个跟内部名称相同的外部名。 Swift为所有属性已提供默认值的且自身没有定义任何构造方法的结构体或基类提供一个默认的构造方法,这个构造方法没有任何参数,并且将简单的创建一个所有属性都设置为默认值的实例。 构造方法可以通过其他构造方法来完成实例的部分构造过程成为构造委托,它能减少多个构造方法间的代码重复。构造委托的实现在值类型和类类型中有所不同,值类型不支持继承构造委托的过程相对简单。 由于类可以继承,所以类类型的构造委托需要保证其所有继承的存储型属性在构造时也能正确的初始化。 2.3 步骤 实现此案例需要按照如下步骤进行。 步骤一:存储型属性的初始值 定义一个用来保存华氏温度的结构体Fahrenheit,拥有一个Double类型的存储属性temperature,通过构造方法给该属性赋初始值,代码如下所示: struct Fahrenheit {
var temperature : Double
init(){
temperature = 32.0
}
}
创建一个Fahrenheit实例,构造方法会自动被调用,运行结果如图-2所示: 图- 步骤二:带参数的构造方法 定义一个包含摄氏度的结构体,包含两个不同的构造方法init(fromFahrenheit)和init(fromKelvin),分别通过接受不同的温度值来创建新的实例,代码如下所示: struct Celsius {
vartemperatureInCelsius:Double = 0.0
init(fromFahrenheitfahrenheit:Double) {
temperatureInCelsius = (fahrenheit - 32.0)/1.8
}
init (fromKevinkelvin:Double) {
temperatureInCelsius = kelvin - 273.15
}
}
创建一个Celsius实例,如果不传参会调用构造方法init(),但是由于没有该方法则会编译报错,运行结果如图-3所示: 图- 步骤三:构造方法的内部参数名和外部参数名 如果在定义构造方法时没有提供参数的外部名称,Swift会为每个构造方法的参数自动生成一个跟内部名称相同的外部名。 定义一个结构体Color,包含三个Double类型的常量属性red、green、blue,分别表示红绿蓝的颜色数值。 Color结构体提供一个构造方法,包含三个Double类型的构造参数,代码如下所示: struct Color {
let red, green, blue : Double
init(red:Double, green:Double, blue:Double){
self.red = red
self.green = green
self.blue = blue
}
}
创建一个Color实例时需要通过三种颜色的外部参数名来传值,如果不通过外部参数名字传值是无法调用该构造方法的,运行结果如图-4所示: 图- 当然也可以使用下划线来忽略外部参数名,代码如下所示: struct Color {
let red, green, blue : Double
init(red:Double, green:Double, blue:Double){
self.red = red
self.green = green
self.blue = blue
}
init(_ red:Double, _ green:Double, _ blue:Double){
self.red = red
self.green = green
self.blue = blue
}
}
在创建Color实例时不通过外部参数名字传值将调用第二个构造方法,运行结果如图-5所示: 图- 步骤四:默认构造方法 Swift为所有属性已提供默认值的且自身没有定义任何构造方法的结构体或基类提供一个默认的构造方法。 定义一个类ShoppingListItem,封装了购物清单中的相关信息:名字name、数量quantity和购物状态purchased. 如果不为该类定义任何构造方法,它将自动获得一个可以为所有属性设置默认值的默认构造方法,对于可选类型的属性name将设置为nil,代码如下所示: classShoppingListItem {
varname:String?
var quantity =
var purchased = false
}
var s = ShoppingListItem()
运行结果如图-6所示: 图- 如果是结构体,还可以自动获得一个逐一成员构造方法,该方法是用来初始化结构体新实例属性的快捷方法。 在调用逐一成员构造方法时通过与成员名相同的参数名进行传值来完成成员属性的初始化,代码如下所示: struct Size {
var width = 0.0
var height = 0.0
}
var size = Size(width: , height: )
运行结果如图-7所示: 图- 步骤五:值类型的构造委托 对于值类型而言可以使用self.init在自定义的构造方法中引用其它的属于相同值类型的构造方法,并且只能在构造方法内部调用self.init。 定义一个结构体Rect用来代表几何矩形,包含一个Point类型的属性origin和一个Size类型的属性size,代码如下所示: struct Point {
var x = 0.0
var y = 0.0
}
struct Size {
var width = 0.0
var height = 0.0
}
structRect {
var origin = Point()
var size = Size()
}
然后使用三种方式提供三个自定义的构造方法: 第一种使用默认值来初始化origin和size,在功能和自动获得的默认构造器是一样的,没有执行任何定制的构造过程; 第二方式使用特定的origin和size实例来初始化,在功能上跟自动获得的逐一成员构造器是一样的; 第三种使用特定的center和size来初始化,先通过 center和size的值计算出origin的坐标,然后再调用init(origin:size)构造方法来将新的origin和size的值赋值给相对应的属性。 代码如下所示: structRect {
var origin = Point()
var size = Size()
init(){}
init(origin:Point,size:Size){
self.origin = origin
self.size = size
}
init(center:Point,size:Size){
letoriginX = center.x-size.width/
letoriginY = center.y-size.height/
self.init(origin: Point(x: originX, y: originY), size:size)
}
}
运行结果如图-8所示: 图- 步骤六:类类型的构造委托 Swift提供两种类型的类构造方法来确保所有类实例中存储属性都能获得初始值,分别是指定构造方法和便利构造方法。 定义三个类Food、RecipeIngredient以及ShoppingListItem,其中Food是基类包含一个String类型的name属性,并提供两个构造方法来创建Food实例,代码如下所示: //类类型的构造委托
class Food {
var name :String
//指定构造方法
init(name :String) {
self.name = name
}
//便利构造方法
convenienceinit(){
self.init(name:"unnamed")
}
}
Food类提供了一个指定构造方法和一个没有参数的便利构造方法,由于Food是基类所以在指定构造方法不需要调用super.init()来完成构造,而便利构造方法则通过指定构造方法给新实例提供一个默认名称,运行结果如图-9所示: 图- RecipeIngredient类是Food的子类,RecipeIngredient类构建了食谱中的一味调味剂,包含一个Int类型的属性quantity,并且定义了两个构造方法来创建RecipeIngredient,代码如下所示: classRecipeIngredient : Food {
var quantity : Int
//指定构造器
init(name: String, quantity:Int) {
//必须先初始化本类定义的属性,才能调用父类的构造器
self.quantity = quantity
super.init(name: name)
//如果需要在子类中给继承来的属性赋值,需要写在super.init的后面
//self.name = name
}
//便利构造器,且覆盖了父类的构造器
override convenience init(name: String) {
self.init(name:name, quantity:)
}
}
RecipeIngredient类的指定构造方法中调用父类的指定构造方法,RecipeIngredient类重写了父类的便利构造方法,并且在内部调用了类中的指定构造方法。 RecipeIngredient类的指定构造方法、便利构造方法以及父类的便利构造方法都可以用来创建RecipeIngredient类的新实例,运行结果如图-10所示: 图- ShoppingListItem类是RecipeIngredient的子类,包含一个Bool类型的属性purchased,默认值是false。ShoppingListItem类另外还包含一个计算属性Description,代码如下所示: classShoppingListItem : RecipeIngredient{
var purchased = false
var description : String {
var output = "\(self.quantity) x \(name)"
output += purchased ? "⎷" : "x"
return output
}
}
ShoppingListItem类的所有属性都有默认值,并且没有定义任何构造器,那么它将继承所有父类中的指定构造器和便利构造器,可以使用全部继承来的构造器创建新的实例,运行结果如图-11所示: 图- 2.4 完整代码 本案例中,完整代码如下所示: importUIKit
//存储属性的初始化
struct Fahrenheit {
var temperature : Double
init(){
temperature = 32.0
}
}
var f = Fahrenheit()
f.temperature
//带参数的构造方法
struct Celsius {
vartemperatureInCelsius:Double = 0.0
init(fromFahrenheitfahrenheit:Double) {
temperatureInCelsius = (fahrenheit - 32.0)/1.8
}
init (fromKevinkelvin:Double) {
temperatureInCelsius = kelvin - 273.15
}
}
var c = Celsius(fromFahrenheit: )
c.temperatureInCelsius
var c2 = Celsius(fromKevin: )
c2.temperatureInCelsius
//构造方法的内部参数名和外部参数名
struct Color {
let red, green, blue : Double
init(red:Double, green:Double, blue:Double){
self.red = red
self.green = green
self.blue = blue
}
init(_ red:Double, _ green:Double, _ blue:Double){
self.red = red
self.green = green
self.blue = blue
}
}
let color = Color(red: , green: , blue: )
let color2 = Color(,,)
//默认构造方法
//class ShoppingListItem {
// varname:String?
// var quantity = 1
// var purchased = false
//}
//var s = ShoppingListItem()
//结构体逐一成员构造方法
struct Size {
var width = 0.0
var height = 0.0
}
var size = Size(width: , height: )
//值类型的构造委托
struct Point {
var x = 0.0
var y = 0.0
}
structRect {
var origin = Point()
var size = Size()
init(){}
init(origin:Point,size:Size){
self.origin = origin
self.size = size
}
init(center:Point,size:Size){
letoriginX = center.x-size.width/
letoriginY = center.y-size.height/
self.init(origin: Point(x: originX, y: originY), size:size)
}
}
varrect = Rect(center:Point(x: , y: ), size:Size(width:,height:))
rect.origin
rect.size
//类类型的构造委托
class Food {
var name :String
//指定构造方法
init(name :String) {
self.name = name
}
//便利构造方法
convenienceinit(){
self.init(name:"unnamed")
}
}
let meat = Food(name: "meat")
meat.name
let food = Food()
food.name
classRecipeIngredient : Food {
var quantity : Int
//指定构造器
init(name: String, quantity:Int) {
//必须先初始化本类定义的属性,才能调用父类的构造器
self.quantity = quantity
super.init(name: name)
//如果需要在子类中给继承来的属性赋值,需要写在super.init的后面
//self.name = name
}
//便利构造器,且覆盖了父类的构造器
override convenience init(name: String) {
self.init(name:name, quantity:)
}
}
let r1 = RecipeIngredient()
let r2 = RecipeIngredient(name: "面")
let r3 = RecipeIngredient(name: "辣椒", quantity:)
classShoppingListItem : RecipeIngredient{
var purchased = false
var description : String {
var output = "\(self.quantity) x \(name)"
output += purchased ? "⎷" : "x"
return output
}
}
let item1 = ShoppingListItem()
let item2 = ShoppingListItem(name: "苹果")
let item3 = ShoppingListItem(name: "泡面", quantity: )