美文网首页
Swift4.0官方文档学习笔记2

Swift4.0官方文档学习笔记2

作者: 优雨 | 来源:发表于2018-07-27 17:16 被阅读9次

    1.repeat-while循环 类似于其他语言里的do while循环

    至于为什么swift为什么要搞这种标新立异的东西 你问苹果工程师去啊

    repeat {
        statements
    } while condition
    

    2.switch语句现在可以用于元组了

    let somePoint = (1, 1)
    switch somePoint {
    case (0, 0):
        print("(0, 0) is at the origin")
    case (_, 0):
        print("(\(somePoint.0), 0) is on the x-axis")
    case (0, _):
        print("(0, \(somePoint.1)) is on the y-axis")
    case (-2...2, -2...2):
        print("(\(somePoint.0), \(somePoint.1)) is inside the box")
    default:
        print("(\(somePoint.0), \(somePoint.1)) is outside of the box")
    }
    

    用于元组时 可以只指定其中一个元素的值,另一个元素用一个常量或变量来接受

    let anotherPoint = (2, 0)
    switch anotherPoint {
    case (let x, 0):
        print("on the x-axis with an x value of \(x)")
    case (0, let y):
        print("on the y-axis with a y value of \(y)")
    case let (x, y):
        print("somewhere else at (\(x), \(y))")
    }
    

    甚至可以像SQL语句一样指定某些元素之间的关系

    let yetAnotherPoint = (1, -1)
    switch yetAnotherPoint {
    case let (x, y) where x == y:
        print("(\(x), \(y)) is on the line x == y")
    case let (x, y) where x == -y:
        print("(\(x), \(y)) is on the line x == -y")
    case let (x, y):
        print("(\(x), \(y)) is just some arbitrary point")
    }
    

    多种情况,用逗号隔开,而不是像OC那样写多个case

    let stillAnotherPoint = (9, 0)
    switch stillAnotherPoint {
    case (let distance, 0), (0, let distance):
        print("On an axis, \(distance) from the origin")
    default:
        print("Not on an axis")
    }
    

    break关键字,只用于需要匹配项直接跳出switch语句的情况

    et numberSymbol: Character = "三"  // Simplified Chinese for the number 3
    var possibleIntegerValue: Int?
    switch numberSymbol {
    case "1", "١", "一", "๑":
        possibleIntegerValue = 1
    case "2", "٢", "二", "๒":
        possibleIntegerValue = 2
    case "3", "٣", "三", "๓":
        possibleIntegerValue = 3
    case "4", "٤", "四", "๔":
        possibleIntegerValue = 4
    default:
        break
    }
    if let integerValue = possibleIntegerValue {
        print("The integer value of \(numberSymbol) is \(integerValue).")
    } else {
        print("An integer value could not be found for \(numberSymbol).")
    }
    

    如果需要用到OC那样的贯穿,用fallthrough关键字显式声明即可

    let integerToDescribe = 5
    var description = "The number \(integerToDescribe) is"
    switch integerToDescribe {
    case 2, 3, 5, 7, 11, 13, 17, 19:
        description += " a prime number, and also"
        fallthrough
    default:
        description += " an integer."
    }
    print(description)
    

    3.guard语句

    guard语句判断其后面的表达式是否为false 如果为false才执行else块的语句
    guard常用于可选项绑定 并且绑定的可选项变量可以在块作用域外使用

    guard let location = person["location"] else {
            print("I hope the weather is nice near you.")
            return
    }
        
    print("I hope the weather is nice in \(location).")
    

    4.版本判断

    @available用于函数、类、协议等可用系统版本的声明,#available用于系统版本的判断

    @available(iOS 11, *)
    func specialFunc() {
      // statement
    }
    
    if #available(iOS 11, *) {
      // your code here
    }
    

    5.形参和实参名可以不同

    这主要是为了增加函数的可读性

    func greet(person: String, from hometown: String) -> String {
        return "Hello \(person)!  Glad you could visit from \(hometown)."
    }
    print(greet(person: "Bill", from: "Cupertino"))
    

    上面的函数第二个参数的形参为hometown,实参为from,调用的时候更像一个句子。

    6.参数的默认值

    指定了默认值的函数参数,在调用的时候就可以省略这个参数。一般来说,有默认值的参数会放在函数参数列表的最后面

    func someFunction(parameterWithDefault: Int = 12) {
        // In the function body, if no arguments are passed to the function
        // call, the value of parameterWithDefault is 12.
    }
    someFunction(parameterWithDefault: 6) // parameterWithDefault is 6
    someFunction() // parameterWithDefault is 12
    

    7.输入输出形式参数

    利用输入输出形式的参数,可以在函数内部改变这个值的同时影响到外部,也就是所谓的传指针
    在将变量作为实际参数传递给输入输出形式参数的时候,直接在它前边添加一个和符号 ( &) 来明确可以被函数修改。
    传入的实参必须声明为var,因为常量是不允许修改的。
    输入输出形式参数也不能有默认值。

    func swapTwoInts(_ a: inout Int, _ b: inout Int) {
        let temporaryA = a
        a = b
        b = temporaryA
    }
    
    var someInt = 3
    var anotherInt = 107
    swapTwoInts(&someInt, &anotherInt)
    

    8.枚举

    • 定义
    enum xxxx {
      // defination
    }
    
    • swift中的枚举不需要被指定一个值 甚至不需要指定具体的类型
    enum CompassPoint {
      case north
      case south
      case east
      case west
    }
    

    在上面的代码中 north south east west都是合法的枚举值,叫做成员值,可以直接使用:

    var directionToHead = CompasssPoint.north
    

    如果变量已经知道是枚举类型的 那么可以省略枚举类型 直接用点语法来设置新值

    directionToHead = .south
    
    • 在switch语句中使用枚举,和OC一样注意全覆盖就行:
    switch directionToHead {
    case .north:
      print("north")
    case .south:
      print("south")
    case .east:
      print("east")
    case .west:
      print("west")
    }
    
    • 通过设置关联值,可以在switch的时候提取到

    9.基本类型(整数,浮点数,布尔量,字符串,数组和字典)、结构体和枚举都是值类型

    也就是说当它被指定到常量或者变量,或者被传递给函数时会被拷贝的类型

    10.类是引用类型

    在引用类型被赋值到一个常量,变量或者本身被传递到一个函数的时候它是不会被拷贝的,而是使用的是同一个对现存实例的引用。

    11.关于结构体的存储属性

    当你把结构体的实例声明为常量时,你不可修改它的存储属性,即时这个存储属性是变量,因为结构体是值类型。但是类的实例没有这种限制,因为类是引用类型。

    12.关于self

    在实例方法中访问自身属性,可以省略self,例如:

    class MyClass {
      var bar = 1
      func foo() {
        print(bar);
      }
    }
    

    如果实例方法中的形参和属性的名称相同的话,会优先使用形参。如果在这时候想使用属性的话,就需要显式使用self以作区分。

    13.修改结构体或枚举的属性

    由于结构体和枚举和类不同,属于值类型,因此无法在实例方法中直接修改属性的值,如果想要实现修改,需要将实例方法声明为异变类型:

    stuct Point {
       var x = 0.0, y = 0.0
       func moveBy(deltaX : Double, deltaY : Double) {
         self.x += deltaX;
         self.y += deltaY;
       }
    }
    
    var somePoint = Point(x:2.0, y:3.0)
    somePoint.moveBy(deltaX:2.0, deltaY:2.0)
    // now somePoint is at (4.0, 5.0)
    

    14.类型方法

    类型方法其实就相当于类方法,用static关键字可以声明一个方法为类型方法,调用的时候不用实例变量而是用类(结构体、枚举)的名称直接来调用。如果你想重写父类的类方法,需要在方法名前加上class关键字

    struct LevelTracker {
        static var highestUnlockedLevel = 1
        var currentLevel = 1
        
        static func unlock(_ level: Int) {
            if level > highestUnlockedLevel { highestUnlockedLevel = level }
        }
        
        static func isUnlocked(_ level: Int) -> Bool {
            return level <= highestUnlockedLevel
        }
        
        @discardableResult
        mutating func advance(to level: Int) -> Bool {
            if LevelTracker.isUnlocked(level) {
                currentLevel = level
                return true
            } else {
                return false
            }
        }
    }```
    ```swift
    class SomeClass {
        class func someTypeMethod() {
            // type method implementation goes here
        }
    }
    SomeClass.someTypeMethod()```
    
    ###15.延迟存储属性
    将一个属性加上`lazy`关键字即可将其定义为延迟存储属性,延迟存储属性会在使用到的时候才进行初始化。
    ```swift
    class DataImporter {
        
        //DataImporter is a class to import data from an external file.
        //The class is assumed to take a non-trivial amount of time to initialize.
        
        var fileName = "data.txt"
        // the DataImporter class would provide data importing functionality here
    }
     
    class DataManager {
        lazy var importer = DataImporter()
        var data = [String]()
        // the DataManager class would provide data management functionality here
    }
     
    let manager = DataManager()
    manager.data.append("Some data")
    manager.data.append("Some more data")
    // the DataImporter instance for the importer property has not yet been created
    
    print(manager.importer.fileName)
    // the DataImporter instance for the importer property has now been created
    // prints "data.txt"
    

    16.类型属性

    • 和类型方法一样,用static声明,并且使用类(结构体、枚举)的名称来访问和修改。
    • 类型属性总是需要给予一个默认值,并且总是自带延迟属性(不需要被声明为lazy),在用到的时候才会初始化。

    17.关于继承

    Swift类没有通用基类,如果你定义一个类并且没有继承自任何类,那他就是一个基类。

    18.关于继承中的重写

    • 重写实例方法,需要加一个override关键字作区分,并且通过super.method()可以在重写的方法中调用父类中的方法
    class Train: Vehicle {
        override func makeNoise() {
            print("Choo Choo")
        }
    }
    
    • 重写属性,通过重写getter/setter实现,通过super.someProperty可以访问父类中的该属性。如果父类存储属性实现了getter和setter,那么子类也必须实现getter和setter。如果父类只实现了getter或者setter,那么子类可以同时实现getter和setter。
    class Car: Vehicle {
        var gear = 1
        override var description: String {
            return super.description + " in gear \(gear)"
        }
    }
    

    19.关于final关键字

    和Java差不多的作用:

    • 将一个实例方法、属性或者下标用final关键字修饰,可以阻止其被子类重写。
    • 将一个类设置为final则这个类将不可继承。

    20.关于初始化器

    • 你需要通过初始化器给所有没有默认值的存储属性都设置一个明确的初始值。
    • 当把一个存储属性设置为可选项的时候,它有一个默认值nil
    • 当所有属性都有默认值(包括可选项)的时候,并且这个类没有父类时,会自动创建一个默认的初始化器,在这种情况下可以不写初始化器,直接通过var instance = Class()来初始化一个类的实例。
    • 就如前面说到过得的一样,结构体会自动生成一个初始化器,接受结构体中的属性作为参数,即使其有默认值也是如此。但是这仅限于没有自己定义初始化器的情况,如果你自定义了别的初始化器,那就不会生成这个默认的初始化器。
    • 由于结构体和枚举是值类型,所以他们不可以被继承,自然就没法使用父类的初始化器,但是可以通过self.init的方式调用本类中的其他初始化器
    • 初始化器分为指定初始化器便捷初始化器两种。其中便捷初始化器是用convinence修饰的初始化器。便捷初始化器只能调用同类中的其他便捷初始化器或者同类中的其他指定初始化器,而不能直接调用父类中的任何初始化器指定初始化器必须从他的父类中调用指定初始化器,而不能调用同类中的任何初始化器
    • 如果子类没有实现任何一个指定初始化器,那么它将自动继承所有的父类指定初始化器。如果子类拥有所有父类指定初始化器的实现(可以通过前面说的方式来继承,也可以通过重写,重写不限制是重写成便利初始化器还是指定初始化器),那么它将自动继承父类所有的便捷初始化器。
    // 基类
    class Food {
        var name: String
        // 以下为一个指定初始化器
        init(name: String) {
            self.name = name
        }
        // 以下为一个便捷初始化器
        convenience init() {
            self.init(name: "[Unnamed]")
        }
    }
    
    // 子类
    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)
        }
    }
    
    let oneMysteryItem = RecipeIngredient() // 从父类继承的便捷初始化器
    let oneBacon = RecipeIngredient(name: "Bacon") // 重写的便捷初始化器
    let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6) // 新增的指定初始化器
    
    class ShoppingListItem: RecipeIngredient {
        var purchased = false
        var description: String {
            var output = "\(quantity) x \(name)"
            output += purchased ? " ✔" : " ✘"
            return output
        }
    }
    
    // 由于子类的新属性拥有默认值 所以不需要新增初始化器 根据规则 所有初始化器都将被继承
    var breakfastList = [
        ShoppingListItem(),
        ShoppingListItem(name: "Bacon"),
        ShoppingListItem(name: "Eggs", quantity: 6),
    ]
    breakfastList[0].name = "Orange juice"
    breakfastList[0].purchased = true
    for item in breakfastList {
        print(item.description)
    }
    // 1 x Orange juice ✔
    // 1 x Bacon ✘
    // 6 x Eggs ✘
    
    • 通过指定init?,并且return nil,使初始化器初始化失败:
    struct Animal {
        let species: String
        init?(species: String) {
            if species.isEmpty { return nil }
            self.species = species
        }
    }
    
    • 可以重写父类的可失败初始化器,让其变成不可失败的。
    • 使用required关键字来表示子类必须实现该初始化器,称为必要初始化器。当然如果子类继承了该初始化器,并且可以满足子类的需求,那么也可以不用显式的实现。在重写父类的必要初始化器时,不需要使用override关键字。
    class SomeClass {
        required init() {
            // initializer implementation goes here
        }
    }
    
    class SomeSubclass: SomeClass {
        required init() {
            // subclass implementation of the required initializer goes here
        }
    }
    

    21.通过闭包设置属性的默认值

    class SomeClass {
        let someProperty: SomeType = {
            return someValue
        }()
    }
    

    闭包的返回值就是属性的默认值,注意闭包后面有一对圆括号,表示立刻执行这个闭包,否则就会变成将这个闭包赋予给这个属性了。

    22.反初始化器

    反初始化器会在实例被释放的时候自动调用,而且不能被手动调用。
    反初始化器没有参数,并且也不用写括号。

    deinit {
        // perform the deinitialization
    }
    

    23.弱引用

    将变量用weak修饰即可将其声明为弱引用对象。
    由于弱引用对象会在没有强引用指向对象的时候讲对象自动置为nil,因此变量必须为可选项。

    class Apartment {
        let unit: String
        init(unit: String) { self.unit = unit }
        weak var tenant: Person?
        deinit { print("Apartment \(unit) is being deinitialized") }
    }
    

    24.无主引用

    对于一定有值的变量,使用unowned关键字来解决循环引用的情况。

    class Customer {
        let name: String
        var card: CreditCard?
        init(name: String) {
            self.name = name
        }
        deinit { print("\(name) is being deinitialized") }
    }
     
    class CreditCard {
        let number: UInt64
        unowned let customer: Customer
        init(number: UInt64, customer: Customer) {
            self.number = number
            self.customer = customer
        }
        deinit { print("Card #\(number) is being deinitialized") }
    }
    

    25.使用捕获列表解决闭包的循环引用问题

    class HTMLElement {
        
        let name: String
        let text: String?
        
        lazy var asHTML: () -> String = {
            [unowned self] in
            if let text = self.text {
                return "<\(self.name)>\(text)</\(self.name)>"
            } else {
                return "<\(self.name) />"
            }
        }
        
        init(name: String, text: String? = nil) {
            self.name = name
            self.text = text
        }
        
        deinit {
            print("\(name) is being deinitialized")
        }
        
    }
    

    捕获列表中的每一项都由 weak 或 unowned 关键字与类实例的引用(如 self )或初始化过的变量(如 delegate = self.delegate! )成对组成。这些项写在方括号中用逗号分开。

    26.可选链

    使用?修饰访问一个可选项,那么继续深入此可选项是比较安全的,其深入访问的所有属性、方法等都将返回一个可选项类型(即使他们本身不是可选项)。

    class Person {
        var residence: Residence?
    }
     
    class Residence {
        var numberOfRooms = 1
    }
    
    let john = Person()
     // ❌强制展开会出错
    let roomCount = john.residence!.numberOfRooms
    // ✅利用可选项链 可以安全的进行后续访问 即使numberOfRooms不是可选项
    if let roomCount = john.residence?.numberOfRooms {
        print("John's residence has \(roomCount) room(s).")
    } else {
        print("Unable to retrieve the number of rooms.")
    }
    

    对于没有返回值的函数来说,其默认返回值为Void,它代表(),即一个空的元组。如果利用可选链,则调用函数会返回一个Void?。因此可以通过检查函数返回值来判断函数是否被成功的调用

    if john.residence?.printNumberOfRooms() != nil {
        print("It was possible to print the number of rooms.")
    } else {
        print("It was not possible to print the number of rooms.")
    }
    

    相关文章

      网友评论

          本文标题:Swift4.0官方文档学习笔记2

          本文链接:https://www.haomeiwen.com/subject/eaugmftx.html