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


      1 1 下标的使用
      2 
      3 1.1 问题
      4 
      5 下标可以定义在类、结构体和枚举中,可以认为是访问对象、集合或序列的快捷方式,不需要再调用实例的特定的赋值和访问方法。
      6 
      7 本案例定义一个Matrix结构体,用于呈现一个Double类型的二维矩阵,其结构体内部使用一个一维数组保存数据,并且定义一个下标用于判断是否会造成数组越界。
      8 
      9 1.2 方案
     10 
     11 首先定义一个Matrix结构体,该结构体有一个存储属性grid,是一个Double类型的结构体,用于存储矩阵的数据。
     12 
     13 Matrix结构体另有两个整型的常量存储属性rows和columns,分别表示矩阵的行数和列数。通过init初始化方法对属性grid、rows以及columns赋初始值。
     14 
     15 然后定义并实现下标运算,将传入的行号row和列号column转换为grid一维数组的下标,获取和设置对应的数据。
     16 
     17 最后定义一个方法indexIsValidForRow,用于判断行号和列号是否越界,通过断言进行判断。
     18 
     19 1.3 步骤
     20 
     21 实现此案例需要按照如下步骤进行。
     22 
     23 步骤一:定义Matrix结构体
     24 
     25 首先定义一个Matrix结构体,该结构体有一个存储属性grid,是一个Double类型的结构体,用于存储矩阵的数据。
     26 
     27 Matrix结构体另有两个整型的常量存储属性rows和columns,分别表示矩阵的行数和列数。通过init初始化方法对属性grid、rows以及columns赋初始值,代码如下所示:
     28 
     29  
     30 struct Metrix {
     31 //保存数据的一维数组
     32 var grid : [Double]
     33 //矩阵的行列
     34 let rows : Int, columns : Int
     35 //初始化方法
     36 init(rows:Int, columns:Int){
     37 self.rows = rows
     38 self.columns = columns
     39 grid = [Double](count: rows*columns, repeatedValue: 0.0)
     40 }
     41 }
     42 步骤二:实现下标运算
     43 
     44 定义并实现下标运算,Marix的下标运算需要两个整型参数row和column,表示二维矩阵的下标,通过行号 row和列号column转换为grid一维数组的下标,获取和设置对应的数据,代码如下所示:
     45 
     46  
     47 struct Metrix {
     48 //保存数据的一维数组
     49 var grid : [Double]
     50 //矩阵的行列
     51 let rows : Int, columns : Int
     52 //初始化方法
     53 init(rows:Int, columns:Int){
     54 self.rows = rows
     55 self.columns = columns
     56 grid = [Double](count: rows*columns, repeatedValue: 0.0)
     57 }
     58 subscript (row:Int,column:Int)->Double {
     59 get {
     60 return grid[row * columns + column]
     61 }
     62 set {
     63 grid[row * columns + column] = newValue
     64 }
     65 }
     66 }
     67 步骤三:判断下标越界
     68 
     69 在结构体中定义一个方法indexIsValidForRow,用于判断行号和列号是否越界,代码如下所示:
     70 
     71  
     72 //判断下标越界
     73 funcindexIsValidForRow (row:Int,column:Int)->Bool {
     74 let index = row * columns + column
     75 return row >= 0 && row <self.rows&& column >= 0 && column <self.columns
     76 }
     77 在下标运算中通过断言进行判断,代码如下所示:
     78 
     79  
     80 subscript (row:Int,column:Int)->Double {
     81 get {
     82 assert(self.indexIsValidForRow(row, column: column), "下标越界")
     83 return grid[row * columns + column]
     84 }
     85 set {
     86 assert(self.indexIsValidForRow(row, column: column), "下标越界")
     87 grid[row * columns + column] = newValue
     88 }
     89 }
     90 然后创建一个Marix实例并进行赋值,运行结果如图-1所示:
     91 
     92 
     93 图-1
     94 
     95 1.4 完整代码
     96 
     97 本案例中,完整代码如下所示:
     98 
     99  
    100 importUIKit
    101 struct Metrix {
    102 //保存数据的一维数组
    103 var grid : [Double]
    104 //矩阵的行列
    105 let rows : Int, columns : Int
    106 //初始化方法
    107 init(rows:Int, columns:Int){
    108 self.rows = rows
    109 self.columns = columns
    110 grid = [Double](count: rows*columns, repeatedValue: 0.0)
    111 }
    112 //判断下标越界
    113 funcindexIsValidForRow (row:Int,column:Int)->Bool {
    114 let index = row * columns + column
    115 return row >= 0 && row <self.rows&& column >= 0 && column <self.columns
    116 }
    117 subscript (row:Int,column:Int)->Double {
    118 get {
    119 assert(self.indexIsValidForRow(row, column: column), "下标越界")
    120 return grid[row * columns + column]
    121 }
    122 set {
    123 assert(self.indexIsValidForRow(row, column: column), "下标越界")
    124 grid[row * columns + column] = newValue
    125 }
    126 }
    127 }
    128 var m = Metrix(rows: 3, columns: 4)
    129 m[0,0] = 100
    130 m[0,1] = 200
    131 m[0,2] = 300
    132 m[0,3] = 400
    133 //下标越界
    134 //m[0,4] = 500
    135 m[1,0] = 600
    136 m[1,1] = 700
    137 隐藏
    138 
    139 2 构造过程
    140 
    141 2.1 问题
    142 
    143 构造过程是为了使用某个类、结构体或枚举类型的实例而进行的准备过程,这个过程包含了为实例中的每个属性设置初始值和为其执行必要的准备和初始化任务。本案例分别演示值类型的构造过程和类的构造过程。
    144 
    145 2.2 方案
    146 
    147 类和结构体在实例创建时,必须为所有存储型属性设置合适的初始值,可以在在构造方法中为存储属性赋初始值,也可以在定义属性时为其设置默认值。构造方法以关键字init命名,最简单的形式是一个不带任何参数的实例方法。
    148 
    149 在定义构造方法时也能提供参数,为构造过程中提供所需要的数据。
    150 
    151 如果在定义构造方法时没有提供参数的外部名称,Swift会为每个构造方法的参数自动生成一个跟内部名称相同的外部名。
    152 
    153 Swift为所有属性已提供默认值的且自身没有定义任何构造方法的结构体或基类提供一个默认的构造方法,这个构造方法没有任何参数,并且将简单的创建一个所有属性都设置为默认值的实例。
    154 
    155 构造方法可以通过其他构造方法来完成实例的部分构造过程成为构造委托,它能减少多个构造方法间的代码重复。构造委托的实现在值类型和类类型中有所不同,值类型不支持继承构造委托的过程相对简单。
    156 
    157 由于类可以继承,所以类类型的构造委托需要保证其所有继承的存储型属性在构造时也能正确的初始化。
    158 
    159 2.3 步骤
    160 
    161 实现此案例需要按照如下步骤进行。
    162 
    163 步骤一:存储型属性的初始值
    164 
    165 定义一个用来保存华氏温度的结构体Fahrenheit,拥有一个Double类型的存储属性temperature,通过构造方法给该属性赋初始值,代码如下所示:
    166 
    167  
    168 struct Fahrenheit {
    169 var temperature : Double
    170 init(){
    171 temperature = 32.0
    172 }
    173 }
    174 创建一个Fahrenheit实例,构造方法会自动被调用,运行结果如图-2所示:
    175 
    176 
    177 图-2
    178 
    179 步骤二:带参数的构造方法
    180 
    181 定义一个包含摄氏度的结构体,包含两个不同的构造方法init(fromFahrenheit)和init(fromKelvin),分别通过接受不同的温度值来创建新的实例,代码如下所示:
    182 
    183  
    184 struct Celsius {
    185 vartemperatureInCelsius:Double = 0.0
    186 init(fromFahrenheitfahrenheit:Double) {
    187 temperatureInCelsius = (fahrenheit - 32.0)/1.8
    188 }
    189 init (fromKevinkelvin:Double) {
    190 temperatureInCelsius = kelvin - 273.15
    191 }
    192 }
    193 创建一个Celsius实例,如果不传参会调用构造方法init(),但是由于没有该方法则会编译报错,运行结果如图-3所示:
    194 
    195 
    196 图-3
    197 
    198 步骤三:构造方法的内部参数名和外部参数名
    199 
    200 如果在定义构造方法时没有提供参数的外部名称,Swift会为每个构造方法的参数自动生成一个跟内部名称相同的外部名。
    201 
    202 定义一个结构体Color,包含三个Double类型的常量属性red、green、blue,分别表示红绿蓝的颜色数值。
    203 
    204 Color结构体提供一个构造方法,包含三个Double类型的构造参数,代码如下所示:
    205 
    206  
    207 struct Color {
    208 let red, green, blue : Double
    209 init(red:Double, green:Double, blue:Double){
    210 self.red = red
    211 self.green = green
    212 self.blue = blue
    213 }
    214 }
    215 创建一个Color实例时需要通过三种颜色的外部参数名来传值,如果不通过外部参数名字传值是无法调用该构造方法的,运行结果如图-4所示:
    216 
    217 
    218 图-4
    219 
    220 当然也可以使用下划线来忽略外部参数名,代码如下所示:
    221 
    222  
    223 struct Color {
    224 let red, green, blue : Double
    225 init(red:Double, green:Double, blue:Double){
    226 self.red = red
    227 self.green = green
    228 self.blue = blue
    229 }
    230 init(_ red:Double, _ green:Double, _ blue:Double){
    231 self.red = red
    232 self.green = green
    233 self.blue = blue
    234 }
    235 }
    236 在创建Color实例时不通过外部参数名字传值将调用第二个构造方法,运行结果如图-5所示:
    237 
    238 
    239 图-5
    240 
    241 步骤四:默认构造方法
    242 
    243 Swift为所有属性已提供默认值的且自身没有定义任何构造方法的结构体或基类提供一个默认的构造方法。
    244 
    245 定义一个类ShoppingListItem,封装了购物清单中的相关信息:名字name、数量quantity和购物状态purchased.
    246 
    247 如果不为该类定义任何构造方法,它将自动获得一个可以为所有属性设置默认值的默认构造方法,对于可选类型的属性name将设置为nil,代码如下所示:
    248 
    249  
    250 classShoppingListItem {
    251 varname:String?
    252 var quantity = 1
    253 var purchased = false
    254 }
    255 var s = ShoppingListItem()
    256 运行结果如图-6所示:
    257 
    258 
    259 图-6
    260 
    261 如果是结构体,还可以自动获得一个逐一成员构造方法,该方法是用来初始化结构体新实例属性的快捷方法。
    262 
    263 在调用逐一成员构造方法时通过与成员名相同的参数名进行传值来完成成员属性的初始化,代码如下所示:
    264 
    265  
    266 struct Size {
    267 var width = 0.0
    268 var height = 0.0
    269 }
    270 var size = Size( 10, height: 20)
    271 运行结果如图-7所示:
    272 
    273 
    274 图-7
    275 
    276 步骤五:值类型的构造委托
    277 
    278 对于值类型而言可以使用self.init在自定义的构造方法中引用其它的属于相同值类型的构造方法,并且只能在构造方法内部调用self.init。
    279 
    280 定义一个结构体Rect用来代表几何矩形,包含一个Point类型的属性origin和一个Size类型的属性size,代码如下所示:
    281 
    282  
    283 struct Point {
    284 var x = 0.0
    285 var y = 0.0
    286 }
    287 struct Size {
    288 var width = 0.0
    289 var height = 0.0
    290 }
    291 structRect {
    292 var origin = Point()
    293 var size = Size()
    294 }
    295 然后使用三种方式提供三个自定义的构造方法:
    296 
    297 第一种使用默认值来初始化origin和size,在功能和自动获得的默认构造器是一样的,没有执行任何定制的构造过程;
    298 
    299 第二方式使用特定的origin和size实例来初始化,在功能上跟自动获得的逐一成员构造器是一样的;
    300 
    301 第三种使用特定的center和size来初始化,先通过 center和size的值计算出origin的坐标,然后再调用init(origin:size)构造方法来将新的origin和size的值赋值给相对应的属性。
    302 
    303 代码如下所示:
    304 
    305  
    306 structRect {
    307 var origin = Point()
    308 var size = Size()
    309 init(){}
    310 init(origin:Point,size:Size){
    311 self.origin = origin
    312 self.size = size
    313 }
    314 init(center:Point,size:Size){
    315 letoriginX = center.x-size.width/2
    316 letoriginY = center.y-size.height/2
    317 self.init(origin: Point(x: originX, y: originY), size:size)
    318 }
    319 }
    320 运行结果如图-8所示:
    321 
    322 
    323 图-8
    324 
    325 步骤六:类类型的构造委托
    326 
    327 Swift提供两种类型的类构造方法来确保所有类实例中存储属性都能获得初始值,分别是指定构造方法和便利构造方法。
    328 
    329 定义三个类Food、RecipeIngredient以及ShoppingListItem,其中Food是基类包含一个String类型的name属性,并提供两个构造方法来创建Food实例,代码如下所示:
    330 
    331  
    332 //类类型的构造委托
    333 class Food {
    334 var name :String
    335 //指定构造方法
    336 init(name :String) {
    337 self.name = name
    338 }
    339 //便利构造方法
    340 convenienceinit(){
    341 self.init(name:"unnamed")
    342 }
    343 }
    344 Food类提供了一个指定构造方法和一个没有参数的便利构造方法,由于Food是基类所以在指定构造方法不需要调用super.init()来完成构造,而便利构造方法则通过指定构造方法给新实例提供一个默认名称,运行结果如图-9所示:
    345 
    346 
    347 图-9
    348 
    349 RecipeIngredient类是Food的子类,RecipeIngredient类构建了食谱中的一味调味剂,包含一个Int类型的属性quantity,并且定义了两个构造方法来创建RecipeIngredient,代码如下所示:
    350 
    351  
    352 classRecipeIngredient : Food {
    353 var quantity : Int
    354 //指定构造器
    355 init(name: String, quantity:Int) {
    356 //必须先初始化本类定义的属性,才能调用父类的构造器
    357 self.quantity = quantity
    358 super.init(name: name)
    359 //如果需要在子类中给继承来的属性赋值,需要写在super.init的后面
    360 //self.name = name
    361 }
    362 //便利构造器,且覆盖了父类的构造器
    363 override convenience init(name: String) {
    364 self.init(name:name, quantity:1)
    365 }
    366 }
    367 RecipeIngredient类的指定构造方法中调用父类的指定构造方法,RecipeIngredient类重写了父类的便利构造方法,并且在内部调用了类中的指定构造方法。
    368 
    369 RecipeIngredient类的指定构造方法、便利构造方法以及父类的便利构造方法都可以用来创建RecipeIngredient类的新实例,运行结果如图-10所示:
    370 
    371 
    372 图-10
    373 
    374 ShoppingListItem类是RecipeIngredient的子类,包含一个Bool类型的属性purchased,默认值是false。ShoppingListItem类另外还包含一个计算属性Description,代码如下所示:
    375 
    376  
    377 classShoppingListItem : RecipeIngredient{
    378 var purchased = false
    379 var description : String {
    380 var output = "(self.quantity) x (name)"
    381 output += purchased ? "" : "x"
    382 return output
    383 }
    384 }
    385 ShoppingListItem类的所有属性都有默认值,并且没有定义任何构造器,那么它将继承所有父类中的指定构造器和便利构造器,可以使用全部继承来的构造器创建新的实例,运行结果如图-11所示:
    386 
    387 
    388 图-11
    389 
    390 2.4 完整代码
    391 
    392 本案例中,完整代码如下所示:
    393 
    394  
    395 importUIKit
    396 //存储属性的初始化
    397 struct Fahrenheit {
    398 var temperature : Double
    399 init(){
    400 temperature = 32.0
    401 }
    402 }
    403 var f = Fahrenheit()
    404 f.temperature
    405 //带参数的构造方法
    406 struct Celsius {
    407 vartemperatureInCelsius:Double = 0.0
    408 init(fromFahrenheitfahrenheit:Double) {
    409 temperatureInCelsius = (fahrenheit - 32.0)/1.8
    410 }
    411 init (fromKevinkelvin:Double) {
    412 temperatureInCelsius = kelvin - 273.15
    413 }
    414 }
    415 var c = Celsius(fromFahrenheit: 88)
    416 c.temperatureInCelsius
    417 var c2 = Celsius(fromKevin: 100)
    418 c2.temperatureInCelsius
    419 //构造方法的内部参数名和外部参数名
    420 struct Color {
    421 let red, green, blue : Double
    422 init(red:Double, green:Double, blue:Double){
    423 self.red = red
    424 self.green = green
    425 self.blue = blue
    426 }
    427 init(_ red:Double, _ green:Double, _ blue:Double){
    428 self.red = red
    429 self.green = green
    430 self.blue = blue
    431 }
    432 }
    433 let color = Color(red: 10, green: 10, blue: 10)
    434 let color2 = Color(10,20,30)
    435 //默认构造方法
    436 //class ShoppingListItem {
    437 // varname:String?
    438 // var quantity = 1
    439 // var purchased = false
    440 //}
    441 //var s = ShoppingListItem()
    442 //结构体逐一成员构造方法
    443 struct Size {
    444 var width = 0.0
    445 var height = 0.0
    446 }
    447 var size = Size( 10, height: 20)
    448 //值类型的构造委托
    449 struct Point {
    450 var x = 0.0
    451 var y = 0.0
    452 }
    453 structRect {
    454 var origin = Point()
    455 var size = Size()
    456 init(){}
    457 init(origin:Point,size:Size){
    458 self.origin = origin
    459 self.size = size
    460 }
    461 init(center:Point,size:Size){
    462 letoriginX = center.x-size.width/2
    463 letoriginY = center.y-size.height/2
    464 self.init(origin: Point(x: originX, y: originY), size:size)
    465 }
    466 }
    467 varrect = Rect(center:Point(x: 20, y: 20), size:Size(20,height:30))
    468 rect.origin
    469 rect.size
    470 //类类型的构造委托
    471 class Food {
    472 var name :String
    473 //指定构造方法
    474 init(name :String) {
    475 self.name = name
    476 }
    477 //便利构造方法
    478 convenienceinit(){
    479 self.init(name:"unnamed")
    480 }
    481 }
    482 let meat = Food(name: "meat")
    483 meat.name
    484 let food = Food()
    485 food.name
    486 classRecipeIngredient : Food {
    487 var quantity : Int
    488 //指定构造器
    489 init(name: String, quantity:Int) {
    490 //必须先初始化本类定义的属性,才能调用父类的构造器
    491 self.quantity = quantity
    492 super.init(name: name)
    493 //如果需要在子类中给继承来的属性赋值,需要写在super.init的后面
    494 //self.name = name
    495 }
    496 //便利构造器,且覆盖了父类的构造器
    497 override convenience init(name: String) {
    498 self.init(name:name, quantity:1)
    499 }
    500 }
    501 let r1 = RecipeIngredient()
    502 let r2 = RecipeIngredient(name: "")
    503 let r3 = RecipeIngredient(name: "辣椒", quantity:5)
    504 classShoppingListItem : RecipeIngredient{
    505 var purchased = false
    506 var description : String {
    507 var output = "(self.quantity) x (name)"
    508 output += purchased ? "" : "x"
    509 return output
    510 }
    511 }
    512 let item1 = ShoppingListItem()
    513 let item2 = ShoppingListItem(name: "苹果")
    514 let item3 = ShoppingListItem(name: "泡面", quantity: 10)
  • 相关阅读:
    NJU 操作系统实验三
    Oracle数据库的下载安装和配置
    实模式/保护模式
    FAT12
    FAT12 img tool
    Mysql InnoDB 数据更新/删除导致锁表
    程序员,你总要有点自己的想法吧!
    支付系统安全设计思维导图
    从实时清分手续费记账看代码的易读性
    (7/8)借助枚举说一下数据类型定义规范
  • 原文地址:https://www.cnblogs.com/52190112cn/p/5052166.html
Copyright © 2020-2023  润新知