[Cmoney 菁英软件工程师战斗营] IOS APP 菜鸟开发笔记(4)

前言

之前po过部分swift语法的笔记,以下再附上近期整理好的部分

类别(class)

  • class没有成员逐一建构器这个功能,而struct有。

    class GameCharacter {
        var attackSpeed = 1.0
        let gender : ""
        var name: String?   // name为一个可选型别String?,会被自动指派为一个预设值nil,表示一开始没有name值。
                            // 这样可以不用在建构时给它值
    
        init(gender : String) { //建构子
           gender = "man" //建构时指派值
        }
    }
    
    
  • 继承:

    class Boss :  GameCharacter {  // Boss继承自GameCharacter
        var name = String? = nil
    
        init(){
            super.init(gender : "girl")
        }
    }
    

建构子

  • 与函式跟方法一样,可以使用其内部参数名称作为外部参数名称,如下:
                // 定义一个结构 有两个建构器
                struct Color {
                    let red, green, blue: Double

                    // 这个建构器有写外部参数名称跟内部参数名称
                    init(red r: Double, green g : Double, blue b: Double) {
                        self.red   = r
                        self.green = g
                        self.blue  = b
                    }

                    // 这个建构器则是合并成一个参数名称 外部跟内部参数名称相同
                    init(white: Double) {
                        red   = white
                        green = white
                        blue  = white
                    }
                }

                var oneColor = Color(red: 0.9, green: 0.5, blue: 0.5)
                var anotherColor = Color(white: 1.0)
  • 与函式跟方法一样,可以省略其外部参数名称,使用 下底线 _ 替代外部参数名称,如下:

        struct SomeNumbers {
            let number: Int
    
            init( _ n: Int) {  // 使用下底线 _ 表示要省略外部参数名称
                number = n
            }
        }
    
        // 生成一个实体时 参数前就不需要有外部参数名称
        var oneNumbers = SomeNumbers(9)
    

结构(structure)

使用时机:

  • 需要封装的资料量较少且较简单。

  • 在指派或传递这个实体时,有特别需求这个资料是会被拷贝而不是参考。

  • 不需要去继承另一个已存在型别的属性或行为。

  • 所有struct都有一个自动生成的成员逐一建构器(memberwise initializer),当要生成一个结构的实体时,用来初始化实体内的属性,如下:

        struct CharacterStats {
            var hp = 0.0
            var mp = 0.0
        }        
       
        var oneStats = CharacterStats(hp: 120, mp: 100) // 建构出实体
        var anotherStats = oneStats

        // 这时修改 anotherStats 的 hp 属性
        anotherStats.hp = 300
        print(anotherStats.hp) // 可以看出来已经改变了 印出:300.0

        // 但 oneStats 的属性不会改变
        // 仍然是被生成实体时的初始值 印出:120.0
        print(oneStats.hp)

属性(property)

  • 被储存於常数的结构不能改变其内变数的值;而被储存於常数的类别可以改变其内变数的值

    // 生成一个 CharacterStats 结构的实体 并指派给一个常数 someStats
    let someStats = CharacterStats(hpValueMax: 900, mpValueMax: 150)
    
    someStats.hpValue = 1200 // 这行会报错误。这个实体 someStats 为一个常数 所以即使 hpValue 为一个变数属性 仍然不能修改hpValue的值
    
    

延迟储存属性

  • 关键字 lazy

  • 只能使用在变数,因为属性的值在实体建构完成之前可能无法得到,而常数属性在建构完成之前必须要有初始值。

  • 使用延迟储存属性可以让类别中如果需要大量计算才能初始化的属性,在需要的时候才真的初始化它

       
        class DataImporter {  // 这个类别会导入外部档案并执行一些操作 初始化可能会花费不少时间
           
            var fileName = "data.txt"   // 这边简化成一个档案名称 实际上可能会有很多操作
        }

        
        class DataManager { // 接着定义另一个类别 DataManager
            // 延迟储存属性
            lazy var importer = DataImporter()
            // 操作时需要用到的资料
            var data = [String]()

            // 简化内部内容 可能还有许多操作资料的动作
        }

     
        let manager = DataManager()   // 生成一个类别 DataManager 的实体常数

        // 添加一些资料
        manager.data.append("Some data")
        manager.data.append("Some more data")

        // 到目前为止 manager 的 importer 都尚未被初始化

        // 直到第一次使用这个属性 才会被创建并初始化
        print(manager.importer.fileName)
  • self 关键字类似 Java 的 this 关键字

  • 覆写属性时,需要使用getter(以及有时可省略的setter)来覆写继承来的属性,且一定要写上属性的名称型别,这样才能确定是从哪一个属性继承而来的。

  • 可以将一个继承来的唯读属性覆写为一个读写属性,但不行将一个读写属性覆写为唯独属性。即原本有setter的话,覆写时就一定要有setter

        class AnotherHunter: Archer {
            // 覆写父类别的属性 重新实作 getter 跟 setter
            override var attackSpeed: Double {
                get {
                    return 2.4
                }
                set {
                    print(newValue)
                }
            }
    
            // 省略其他内容
        }
    

错误处理

  • Swift 提供了 do-catch 机制来拦截程序执行时发生的错误。

    do{
    	//会有例外丢出的函数呼叫放这
    	let fm = FileManager.default
    	try fm.removeItem(atPath:"档名") //这边要加 try
    } catch {
    	//抓到错误时的处理
    	print(error.localizedDescription) //印出错误讯息
    }
    
  • 如果想 自订错误的类型,必须用 enum 定义。以下为遵从 Error protocol 的型别 GoAfterGirlError,用它来表达追求女生失败的各种原因:

        enum GoAfterGirlError:Error {
            case poorProblem
            case tooYoungProblem
            case notAquariusProblem
        }
    
    • 当程序判断错误发生时,必须用关键字 throw 丢出错误。而且唯有乖乖遵从 Error protocol 的型别,才能被当成错误丢出。

    • 而当 function 里的程序码有可能丢出错误时,这个 function 的定义还必须加上 throws,加在( ) 後,警告大家它很危险,有可能丢出错误 :

        func goAfterAngelababy(money:Int, age:Int) throws {
             guard money > 10000 else {
                throw GoAfterGirlError.poorProblem
            }
    
            guard age > 18 else {
                throw GoAfterGirlError.tooYoungProblem
            }
    
            print("我追到 Angelababy 了!")
        }
            //当我们呼叫的 function 有可能丢出错误,也就是它有加上 throws 时,我们还要补上 try 才能呼叫。
            try goAfterAngelababy(money: 1000, age: 30)
    
  • Swift 强制要求我们错误一定要处理,不处理将产生 compile error 提醒我们。

  • 如果函式有回传值,而且可能返回 nil 的话,可以使用 try? 来呼叫函式,避免 APP 当掉

    let ans = try? divided(10, by:0) //divided 函式如果分母为0时,会抛出错误,所以这边会回传nil
    

<<:  Day 17 - 卷积神经网络 CNN (2)- 战国时代之版图扩张

>>:  网站架设攻略,初阶观念厘清!

视觉化当日趋势图(5)-折线图/趋势图前置

我打算针对成交价用折线图做一个视觉化的呈现, 而开始撰写前有一些必要的JS需要了解, 第一个是jQu...

第30天:『SEO优化第十二步』- Google Search Console的成效分析

SEO优化-成效分析 Google Search Console的成效,主要针对访客对关键字的点击次...

[Golang]go test指令说明-心智图总结

1. -cpu a. 用途: 模拟程序在不同CPU核心数的计算机,效能表现。 b. 用来设定测试执行...

铁人赛27天scss杂纪

今天还是想想不到写啥,所以只能又来继续骗天数罗,今天就是会把原本笔记上面的东西,没有提到部分记录在这...

DAY29-ASP.NET网页切换导向及状态管理-趴水

ASP.NET网页切换导向及状态管理-趴水 今天来做做看 在网页输入资料後 按下按钮 可以将资料导向...