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

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.")
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351

推荐阅读更多精彩内容

  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明先生_X自主阅读 15,975评论 3 119
  • 控制流 Swift提供了各种控制流程语句。这些包括while循环多次执行任务; if,guard以及switch基...
    Fuuqiu阅读 368评论 0 0
  • Move语义 copy数据量大的对象很昂贵. 解决问题的方法有两种: shallow copy或者deep cop...
    Elinx阅读 213评论 0 0
  • 如果我存在的意义是为他人带来伤害的,最后连自己都无法自拔的陷入绝境,那么还是让我真正的离开这个世界吧,管不了那么多...
    Lovecomelive阅读 155评论 0 1
  • 陈守坤 关于师说 关于教育 从孔子到叶圣陶 从苏格拉底到杜威 那些滚烫的字符 一碰触 心湖就会泛起涟漪 孔子说 因...
    坤哥_80a2阅读 407评论 0 1