美文网首页
Swift3.0 总结

Swift3.0 总结

作者: KeepFighting | 来源:发表于2016-12-14 09:13 被阅读154次
图片.png

3.0[TOC]

变量和常量

声明常量和变量

var  Name =  Value
//: Integer
var hours = 24

//: Double
var PI = 3.14

//: Bool
var swiftIsFun = true

//: String
var swift = “swift”
// tuple 元组类型,可按索引访问成员,用在函数上,可以有多个返回值,很好用
var me = (“James”, 18, "11@session.cn”)
me.0
me.1
  • Type annotation 类型注释
    通过:Type指定变量类型的形式,在Swift中叫做Type annotation。比如
var x: Int
var s: String
  • Type inference 类型推导
    Swift编译器会根据我们为变量的赋值自动推导变量的类型,这个特性,在Swift里叫做Type Inference
  • 输出常量和变量
    用 let 修饰,表示常量不可被修改
let minutes = 30

用 var 修饰,表示变量,可被修改

var fireIsHot = true

整数与浮点数

  • Swift编译器会根据目标编译平台,把Int或UInt转换成对应的整数类型,在64位平台上,分别使用min和max方法,来查看Int和Int64可以表达的数值范围:
Int.min   // -9223372036854775808
Int.max   // 9223372036854775807
Int64.min // -9223372036854775808
Int64.max // 9223372036854775807

let fifteenInDecimal = 15  10进制
let fifteenInHex = 0xF  16进制
let fifteenInOctal = 0o17 8进制
let fifteenInBinary = 0b1111 2进制

在数字中,使用分隔符:

let million = 1_000_000  等价于 1000000
  • Float: 最多表达6位精度的浮点数;
  • Double: 至少可以表达15位精度的浮点数;
    如不是有明确的需求,我们应该统一使用Double来定义浮点数
    在Swift里,我们使用一个整数,编译器会把它推导成Int,使用一个浮点数,编译器会把它推导成 Double
var three = 3
type(of: three) // Int.Type
var zeroPointForteen = 0.14
type(of: zeroPointForteen) // Double.Type

不同类型数字的字面值直接进行运算(类型转换格式 Double(Value))

PI = Double(three) + zeroPointForteen

Swift中的字符串

Swift中,我们可以使用\u{1F496}这样的方式,来表示一个unicode scalar :

Swift里,String已经彻底不再是一个集合类型。而是一个提供了从多个维度展现一个Unicode视图的类型。你可以得到它的多个Characters,可以看到它的UTF-8
/ UTF-16 / Unicode scalar值等等
面对unicode复杂的组合规则,我们很难保证所有的集合算法都是安全并且语义正确的。为了解决这个问题,Swift中的String不是一个集合类型。

  • 理解表达字符串的view
    因为字符串看上去就一个一连串单个字符的组合。所以,Swift的开发者还是决定让这个类型用起来像一个集合类型。
    为了达到这个目的,第一个要解决的就是,如何让Swift理解字符串中的“字符集合”。为了不存在歧义,String为开发者提供了一些不同的"view"。简单来说,就是告诉String类型如何去理解字符串中的内容,它们是String的不同属性。

  • unicode scalar的编码方式划分的“view”
    unicodeScalar:按照字符串中每一个字符的unicode scalar来形成集合;
    utf8:按照字符串中每一个字符的UTF-8编码来形成集合;
    utf16:按照字符串中每一个字符的UTF-16编码来形成集合;

let cafee = "caf\u{0065}\u{0301}" // café
cafee.unicodeScalars.dropLast(1) // cafe  使用unicodeScalars这个view,移除最后一个元素
cafee.utf16.dropLast(1)          // cafe   使用utf16这个view,移除最后一个元素
cafee.utf8.dropLast(1)          // cafe      使用utf8这个view,移除最后一个元素 ,由于声调符需要两个UTF-8编码,因此最后一个会留意下无法识别的乱码
  • String的另外一个view:characters
    它是一个String.CharacterView类型的属性。这个“view”是按照unicode grapheme clusters计算字符串的字符个数,也就是最接近我们肉眼看到的字符的view。因此String.characters形式上就可以理解为“由我们看到的字符构成的字符数组”。
  • 获取前缀
    不建议使用 cafee[0 ..< 3] 来获取前缀,因为这样的操作的复杂度是 O(n2)的,Swift提供了一个叫做prefix(_:)的方法,它返回一个特定的CharacterView:
String(cafee.characters.prefix(3)) // Caf
  • 遍历字符串中的每一个字符
var mixStr = "Swift很有趣"
for (index, value) in mixStr.characters.enumerated() {
    print("\(index): \(value)")
}

⁃ 插入内容

if let index = mixStr.characters.index(of: "很") {
    mixStr.insert(contentsOf: " 3.0".characters, at: index)
    // "Swift 3.0很有趣"
}
  • 基于Range的查找和替换
if let cnIndex = mixStr.index(of: "很") {
    // 2. Replace a specific range
    mixStr.replaceSubrange(
        cnIndex ..< mixStr.endIndex,
        with: " is interesting!")
    // Swift 3.0 is interesting!
}
  • 字符串切片
let swiftView = mixStr.characters.suffix(12).dropLast()
String(swiftView) // interesting

let strViews = mixStr.characters.split(separator: " ")  //按空格分割
strViews.map(String.init)  // 将数组每个元素生成字符串对象
// ["Swift", "3.0", "is", "interesting!"]

// 通过闭包指定分割条件 :每个奇数位置的字符当成分隔符
var i = 0
let singleCharViews = mixStr.characters.split { _ in
    if i > 0 {
        i = 0
        return true
    }
    else {
        i += 1
        return false
    }
}

singleCharViews.map(String.init)  // ["S", "i", "t", "3", "0", "i", " ", "n", "e", "e", "t", "n", "!"]
  • Tuple 元组类型
    定义方式:
let success = (200, "HTTP OK")  //

不指定参数名称方式,只能通过下标访问

success.0
success.1
let me = (name: “Jams”, no: 18, email: “session.cn”) //指定参数名称方式
可以通过参数名称访问
me.name
me.no
me.email
  • Tuple Decomposition
    把一个Tuple的值,一一对应的拆分到不同的变量上
var (successCode, successMessage) = success
print(successCode) // 200
print(successMessage) // HTTP OK
修改部分值: “_”表示
let (_, errorMessage) = fileNotFound
print(errorMessage)
  • tuple 的比较:只有元素个数相同的Tuple变量之间(最多包含6个元素的Tuple变量进行比较,超过这个数量,Swift会报错)
let tuple11 = (1, 1)
let tuple12 = (1, 2)
tuple11 < tuple12 // true
  • 运算操作符
    • Swift 3不再允许浮点数取模。
      例如:8 % 2.5这样的写法在Swift 3中将会报错。如果要对浮点数取模,只能这样: 8.truncatingRemainder(dividingBy: 2.5)
    • Swift不会把数字自动转换成Bool类型。在需要Bool值的地方,你必须明确使用一个Bool变量。
    • Swift 3中不再支持自增(++)和自减(--)操作符,使用它们的前缀和后缀版本都会得到一个编译器错误。因此,需要+1/-1的时候,只能使用b += 1 / b -= 1来实现。
    • Swift特有的操作符,用来处理和Optional有关的判断
var userInput: String? = "A user input"
let value = userInput ?? "A default input"
如果opt是一个optional,当其不为Nil时,就使用optional变量自身的值,否则,就使用??后面的“默认值”
  • 闭区间
// begin...end
for index in 1...5 {
    print(index)
}
  • 半开半闭区间
// begin..<end [begin, end)
for index in 1..<5 {
    print(index)
}

Control Flow

  • 条件分支判断语句
switch light {
    case "red":
        action = "stop"
    case "yellow":
        action = "caution"
    case "green":
        action = "go"
    default:
        action = "invalid"
}

和C++/Java这样语言相比:

  • 每个case语句不会自动“贯通”到下一个case,因此我们也无需在每个case最后一行写break表示结束
  • 必须考虑default情况,不写编译器会报错;明确表示你考虑到了其他的情况,只是你不需要更多额外处理而已

循环控制语句

遍历一个集合类型或者范围
let vowel = ["a", "e", "i", "o", "u"]
for char in vowel {
    print(char)
}
// aeiou

for number in 1...10 {
    print(number)
}
// 12345678910

传统C风格的三段式for循环,已经在Swift 3中被移除
// for var i = 0; i < 10; i += 1 {
//    print(i)
// }

var i  = 0
while i < 10 {
    print(i)
    i += 1
}

// do ... while
repeat {
    print(i)
    i -= 1
} while i > 0

匹配值的方式

  • case 匹配的值 = 要检查的对象

    let origin = (x: 0, y: 0)
    if case (0, 0) = pt1 {
    print("@Origin")
    }
    用(_, 0)和(0, _)表示忽略掉_的部分,仅对tuple中某一部分的值进行匹配
    
  • 循环语句,用于控制循环条件

    let array1 = [1, 1, 2, 2, 2]
    for case 2 in array1 {
    print("found two") // Three times
    }
    
  • 把匹配的内容绑定到变量 value binding

    switch pt1 {
    case (let x, 0):
        print("(\(x), 0) is on x axis")
    case (0, let y):
        print("(0, \(y)) is on y axis")
    default:
    break
    }
    
  • 绑定enum中的关联值

    enum Direction {
    case north, south, east, west(abbr: String)
    }
    
    let west = Direction.west(abbr: "W")
    if case .west = west {
    print(west) // west("W")
    }
    访问enum的关联值
    if case .west(let direction) = west {
    print(direction) // W
    }
    
  • 自动提取optional的值

    let skills: [String?] =
    ["Swift", nil, "PHP", "JavaScirpt", nil]
    
    打印出不为nil的元素
    for case let skill? in skills {
    print(skill) // Swift PHP JavaScript
    }
    
  • 自动绑定类型转换的结果

    let someValues: [Any] = [1, 1.0, "One"]
    for value in someValues {
        switch value {
            case let v as Int:
                print("Integer \(v)")
            case let v as Double:
                print("Double \(v)")
            case let v as String:
                 print("String \(v)")
         default:
                print("Invalid value")
        }
    }
    // Integer 1
    // Double 1.0
    // String One
    仅仅想判断类型,而不需要知道具体内容
    for value in someValues {
        switch value {
            case is Int:
            print("Integer")
            // omit for simplicity...
        }
    }
    
  • 使用where约束条件

    for i in 1...10 where i % 2 == 0 {
        print(i)
    }
    
    搭配switch
    switch battery {
    case .normal(let percentage) where percentage <= 0.1:
        print("Almost out of power")
    case .normal(let percentage) where percentage >= 0.8:
        print("Almost fully charged")
    default:
        print("Normal battery status")
    }
    
  • 使用逗号串联条件

    switch halfPower {
    // ...
    case .fullyCharged, .outOfPower
        print("Fully charged or out of power")
    // ...
    }
    
    if case .normal(let percentage) = battery,
             case 0...0.1 = percentage {      // 要比较的值的范围是写在等号左侧,而不是 percentage =  case 0...0.1
        print("Almost out of power")
    }
    
    if嵌套
    应该这样写
    if A, B, C {
    
    }
    不应该这样写
    if A {
        if B {
            if C {
    
            }
        }
    }
    
  • tuple简化多个条件的比较

    //原始方式
    let username = "ssession.cn"
    let password = 11111111
    if username == "ssession.cn" && password == 11111111 {
        print("correct")
    }
    //更简洁的方式
    if case ("ssession.cn", 11111111) = (username, password) {
        print("correct")
    }
    

Swift 3 Collections

Array

  • 添加和删除元素
array1.append(1)    // [1]
array1 += [2, 3, 4]  // [1, 2, 3, 4]
// [1, 2, 3, 4, 5]
array1.insert(5, at: array1.endIndex)
array1.remove(at: 4) // [1, 2, 3, 4]
array1.removeLast() // [1, 2, 3]
array2.removeLast() // This will crash!!! 要对removeLast()的应用安全负责,当你删除一个空数组中最后一个元素的时候,会直接引发运行时错误。
  • 值语义实现的Array 复制一个Array对象时,会拷贝整个Array的内容;如果仅仅复制了Array而不对它修改时,真正的复制是不会发生的,两个数组仍旧引用同一个内存地址。只有当你修改了其中一个Array的内容时,才会真正让两个Array对象分开。

  • 绝大多数时候,不需要[] 访问元素,使用[]访问元素,一旦数组越界直接崩溃

  • 访问数组中的每一个元素:
a.forEach { print($0) }
// or
for value in a {}
  • 获得数组中每一个元素的索引和值:
for (index, value) in a.enumerated() {}
  • 查找数组中元素的位置时(例如,查找等于1的元素的索引):
a.index { $0 == 1 }
  • 过滤数组中的某些元素:
a.filter { $0 % 2 == 0 }
  • 通过closure(闭包)参数化对数组元素的变形操作
    简单的Fibonacci序列:[0, 1, 1, 2, 3, 5]。如果我们要计算每个元素的平方
    最朴素的做法是for循环:
var fibonacci = [0, 1, 1, 2, 3, 5]
var squares = [Int]()

for value in fibonacci {
    squares.append(value * value)
}
  • 更简洁的方式: 闭包
// [0, 1, 1, 4, 9, 25]
let constSquares = fibonacci.map { $0 * $0 }  // “$”:Swift 内建的用来代表参数的简写 $0 ; map :对数组每个元素执行该闭包

参数化数组元素的执行动作
fibonacci.min() // 0
fibonacci.max() // 5   使用min和max很安全,因为当数组为空时,这两个方法将返回nil。
  • 比较数组
// false
fibonacci.elementsEqual([0, 1, 1], by: { $0 == $1 })
// true
fibonacci.starts(with: [0, 1, 1], by: { $0 == $1 })
  • 对数组进行排序
/ [0, 1, 1, 2, 3, 5]
fibonacci.sorted()
// [5, 3, 2, 1, 1, 0] 从大到小 ,>是{ $0 > $1 }的简写形式。
fibonacci.sorted(by: >)

let pivot = fibonacci.partition(by: { $0 < 1 })  //partion(by:)则会根据指定的条件返回一个分界点位置
// [0, 1, 1]
fibonacci[0 ..< pivot]
// [2, 3, 5]
fibonacci[pivot ..< fibonacci.endIndex]
  • 把数组的所有内容,“合并”成某种形式的值
    fibonacci.reduce(0, +) // 12 ;初始值是0,和第二个参数+,则是{ $0 + $1 }的缩写。
    

Dictionary

  • 定义方式:[KeyType: ValueType]
enum RecordType {
    case bool(Bool)
    case number(Int)
    case text(String)
}

let record11: [String: RecordType] = [
    "uid": .number(11),
    "exp": .number(100),
    "favourite": .bool(true),
    "title": .text("Dictionary basics")
]
  • 更新value的时候,同时获得修改前的值
record10.updateValue(.bool(true),
    forKey: "favourite") // .bool(false)
  • 添加元素
record10["watchLater"] = .bool(false)
  • 删除特定的key
record10["watchLater"] = nil
  • 遍历Dictionary
for (k, v) in record10 {
    print("\(k): \(v)")
}
record10.forEach { print("\($0): \($1)") }

Set的代数运算

var setA: Set = [1, 2, 3, 4, 5, 6]
var setB: Set = [4, 5, 6, 7, 8, 9]
  • 取出相同的部分
let interSectAB: Set = setA.intersection(setB)// {5, 6, 4}
  • 去除相同部分
let symmetricDiffAB: Set = setA.symmetricDifference(setB) // {9, 7, 2, 3, 1, 8} 
  • 整合
let unionAB: Set = setA.union(setB) // {2, 4, 9, 5, 6, 7, 3, 1, 8}
  • 去除掉与B相同的部分
let aSubstractB: Set = setA.subtracting(setB) // {2, 3, 1}
  • 可修改Set自身”,加上form
setA.formIntersection(setB) // { 5, 6, 4 }  

Optional

对各种值为"空"的情况处理不当,几乎是所有Bug的来源,Swift里,明确区分了"变量"和"值有可能为空的变量"这两种情况。

let number: Int? = 1 // 类型后面加 ?,表示Optional类型,即值可能为空

if let 形式

if let number = number {
    print(number)
} // 第一个 number(if代码块中新定义的变量)的类型已被解包,也就是类型是 Int;如果 number的值不为nil,就  print(number)

if let `number` = number, `number` % 2 != 0 {
    print(number)
} // number % 2 != 0中的number,指的是在if代码块中新定义的变量

从某个url加载一张jpg的图片,可以这样实现。
if let url = URL(string: imageUrl), url.pathExtension == "jpg",
    let data = try? Data(contentsOf: url),
    let image = UIImage(data: data)
{
    let view = UIImageView(image: image)
}

while let 形式

let numbers = [1, 2, 3, 4, 5, 6]
var iterator = numbers.makeIterator()
//遍历一个数组
while let element = iterator.next() {
    print(element)
}

guard 形式

if let 形式,解包的变量的作用域仅仅在if let{}括号内,guard 可以解决这种弊端。

如果条件不成立,执行 else 后面的 {},如果成立,则会 print(first),且 `first`的作用域在`{}`下面都有效,且已经解包
func arrayProcess(array: [Int]) {
    guard let first = array.first else `{`
        return
    `}`

    print(`first`)
}

optional chaining 可选链

绝大多数时候,如果你只需要在optional不为nil时执行某些动作,使用 optional chaining

var swift: String? = "Swift"
let SWIFT: String

//optional chaining 形式,简洁
let SWIFT = swift?.uppercased() // Optional("SWIFT")

//而不需要这样写,显得啰嗦
if let swift = swift {
    SWIFT = swift.uppercased()
}
else {
    fatalError("Cannot uppercase a nil")
}

force unwrapping 强制解包

对于一个optional变量来说,可以用!来强行读取optional包含的值,但是强制读取值为nil的optional会引发运行时错误


func 和 closure

func

函数声明格式

func funcName( [param list] ) { /*Body*/ }

func multipleOf(multiplier: Int, andValue: Int) {
    print("\(multiplier) * \(andValue) = \(multiplier * andValue)")
}

调用

multipleOf(5, andValue: 10)

InnerName 与 OuterName

InnerName :参数的内部名字,来帮助函数的实现者实现函数逻辑
OuterName:函数的调用者使用OuterName向函数传递参数
默认情况下,函数参数的InnerName和OuterName是相等的;
函数的第一个参数,它默认是没有OuterName
可以强行指定一个OuterName

func createTable(rowNumber `row`: Int, colNumer `column`: Int) {
    print("Table: \(row) x \(column)")
}
这里 `innerName` 分别是 `row`,`column`,outerName分别是 rowNumber,colNumber
createTable(rowNumber:10, colNumber:10)

closure �闭包

{ ( param list ) -> RetType in
/* Closure body */
}
闭包组成的元素有 参数,返回值,方法体; 用 in 关键字 将 参数,返回值 和 方法体 分成2部分。

var addClosure: (Int, Int) -> Int =
{ (a: Int, b: Int) -> Int in
    return a + b
}  //  这个闭包类型是:接收2个Int类型参数,返回值是 Int, 方法体是   return a + b

闭包的简化

addClosure已经明确要求它“接受两个Int参数,返回Int”,因此,我们完全可以通过type inference,去掉closure定义中的类型和返回值描述:

addClosure = { a, b in return a + b }

Closure只包含一条执行语句,我们可以省略掉return(Single Expression Closure)

addClosure = { a, b in a + b }

Swift提供了一种描述Closure参数名的方法:$0, $1, $2; $0表示第一个参数,$0表示第二个参数,再次简化

addClosure = { $0 + $1 }

闭包更多的是直接实现在函数的参数位置

func execute(a: Int,
    _ b: Int,
    operation: (Int, Int) -> Int) -> Int {
    return operation(a, b)
} //函数的第三个参数是一个闭包

闭包直接实现在函数的参数位置

execute(1, 2, operation: { (a: Int, _ b: Int) -> Int in
    return a + b
})

Trailing Closures 尾随闭包

如果函数类型参数是函数的最后一个参数,可以把closure写在函数调用的外面

execute(1, 2) { $0 + $1 }

Capturing Values

当closure访问一个变量时,它就会“捕获”这个变量,即便离开了这个变量的作用域,这个变量仍旧是存在并且可以访问的,就像是closure把这个变量“关在了closure内部"一样。

func counting() -> () -> Int {
    var count = 0  // 临时变量 count 被闭包捕获了
    let incrementCount: () -> Int = { ++count }

    return incrementCount
}

let c1 = counting()
c1()       //1   每调用一次 count 加1
c1()       //2

let c2 = counting()

c2()       //1
c2()  //2
c2()  //3

Protocol

格式

protocol Engine {
    func start()
    func stop()
}

添加属性

在protocol中添加属性的时候,必须明确指定该属性支持的操作:只读(get)或者是可读写(get set)。

protocol Engine {
    var cylinder: Int { get set }

    func start()
    func stop()
    func getName(label: String)
    func getName()
}

protocol也可以继承


---

protocol TurboEngine : Engine {
    func startTurbo()
    func endTurbo()
}

computed property

尽管在protocol中,Engine.cylinder被定义成了一个computed property,但是我们在实现的时候,却可以把它定义为一个简单的stored property:

class V8 : TurboEngine {
    var cylinder = 8 
}

Swift的标准库的protocol

protocol - Equatable

struct Rational {
    var numerator: Int
    var denominator: Int
}

要判断2个Rational 是否相等,需要遵守 Equatable协议

extension Rational: Equatable {}

func == (lhs: Rational, rhs: Rational) -> Bool {
    let equalNumerator = lhs.numerator == rhs.numerator
    let equalDenominator = lhs.denominator == rhs.denominator
    return equalNumerator && equalDenominator
}

protocol - Comparable 它继承自Equatable

extension Rational: Comparable {}
func < (lhs: Rational, rhs: Rational) -> Bool {
    let lQuotient =
        Double(lhs.numerator) / Double(lhs.denominator)
    let rQuotient =
        Double(rhs.numerator) / Double(rhs.denominator)

    return lQuotient < rQuotient
}

定义了"<",Swift就会自动推导">";定义了"==",Swift就会自动推导"!=“。因此,只实现"<"和"==“就可以了。

基于Array的操作

把Rational对象放入支持Comparable的集合类型时,例如Array,集合的各种排序,比较,包含操作,就可以对Rational对象生效了

var rationals: Array<Rational> = []
for i in 1...10 {
    var r = Rational(numerator: i, denominator: i+1)
    rationals.append(r)
}

print("Max in rationals: \(rationals.maxElement()!)")
print("Min in rationals: \(rationals.minElement()!)")
rationals.startsWith([oneHalf])
rationals.contains(oneHalf)

protocol -CustomStringConvertible 自定义类型的print

extension Rational : CustomStringConvertible {
    var description: String {
        return "\(self.numerator) / \(self.denominator)"
    }
}

protocol- Hashable 实现这个protocol才可以作为Dictionary或者Set的元素

extension Rational: Hashable {
    var hashValue: Int {
        let v = Int(String(self.numerator) + String(self.denominator))!
        return v
    }
}

let oneHalf = Rational(numerator: 1, denominator: 11)

var dic: Dictionary<Rational, String> = [oneHalf: "1/2"]
var rSet: Set<Rational> = [oneHalf]

protocol可以提供默认实现

声明一个 Flight protocol

protocol Flight {
    var delay: Int { get }
    var normal: Int { get }
         var flyHour: Int { get }
     func delayRate() -> Double
}

额外添加属性

通过定义一个protocol extension来实现,totalTrips的默认实现是返回 delay + normal

extension Flight {
    var totalTrips: Int {
        return delay + normal
    }
}

给已有方法提供默认实现

extension Flight {
    func delayRate() -> Double {
        return Double(delay) / Double(totalTrips)
    }
}

where限定

通过 where来限定 某个类型同时遵从OperationalLife和Flight,才能使用maxFlyHours属性

protocol OperationalLife {
    var maxFlyHours: Int { get }
}
extension Flight where Self: OperationalLife {
    func isInService() -> Bool {
        return self.flyHour < maxFlyHours
    }
}

自定义类型

struct

struct作为一个值类型,Swift默认不允许我们在method里修改成员的值,如果我们要修改它,需要在对应的方法前面使用mutating关键字。
编译器会给struct提供默认的init方法

struct Location {
    let x: Double
    var y: Double

    // Initializer
    init(stringPoint: String) {
        // "100,200"
        let xy = stringPoint.characters.split(",")
        x = atof(String(xy.first!))
        y = atof(String(xy.last!))
    }
    
    mutating func moveHorizental(dist: Double) {
        self.x = self.x + dist;
    }
}
var pointA = Location("100,200”) // 使用字符串初始化

Enumeration 枚举

  • 可以指定rawValue
enum Direction: Int {  // 默认把EAST / SOUTH / WEST / NORTH“绑定”上0 / 1 / 2 / 3
    case EAST
    case SOUTH
    case WEST
    case NORTH
}

//访问rawValue
let north = Direction.NORTH.rawValue
let jan = Month.January.rawValue
//通过rawValue生成enum
let north = Direction(rawValue: 4)
  • Associated values

可以给每一个case“绑定”不同类型的值

enum HTTPAction {
    case GET
    case POST(String) 
    case PUT(Int, String)
}
var action1 = HTTPAction.GET
var action2 = HTTPAction.POST("BOXUE")

switch action1 {
case .GET:
    print("HTTP GET")
case let .POST(msg):
    print("\(msg)")
case .DELETE(let id, let value):
    print("\(id)=\(value)")
}

- 不是每一个case必须有associated value,例如.GET就只有自己的enum value;
- 当我们想“提取”associated value的所有内容时,我们可以把let或var写在case后面,例如.POST的用法;
- 当我们想分别“提取”associated value中的某些值时,我们可以把let或var写在associated value里面,例如.DELETE的用法;

属性

  • Computed properties

在每次被访问的时候,要被计算出来,而不是内存中读取出来。比如例子的center属性

struct MyRect {
    var origin: Point
    var width: Double
    var height: Double

    var center: Point {
        get {
            let centerX = origin.x + self.width / 2
            let centerY = origin.Y + self.height / 2

            return Point(x: centerX, y: centerY)
        }
        set(newCenter) {
            self.origin.x = newCenter.x - self.width / 2
            self.origin.y = newCenter.y - self.height / 2
        }
    }
}
  • Property observer

willSet和didSet,它们分别在stored property被赋值前和后被调用

struct MyRect {
    var origin: Point
    var width: Double {
                 willSet(newWidth) {
            print("width will be updated")
        }
        didSet(oldWidth) {
            if width <= 0 {
                width = oldWidth
            }
            else {
                self.height = width
            }
        }
    }
}
- 在didSet里,我们可以直接使用width读取MyRect的width属性,但是我们必须使用self读取其它的属性;
- willSet和didSet不在对象的init过程中生效,仅针对一个已经完整初始化的对象在对属性赋值的时候生效;
- 如果我们不指定willSet和didSet的参数,Swift默认使用newValue和oldValue作为它们的参数;
  • Type property

除了对象属性之外,有些属性是属于一个类型的,它们并不和具体的对象相关

enum Shape {
    case RECT
    case TRIANGLE
    case CIRCLE
}

struct MyRect {
    var origin: Point
    var width: Double
    var height: Double

    // Type property 描述一个类型所有对象的属性
    static let shape = Shape.RECT
}

let shape = MyRect.shape  // 直接使用类型的名字访问

内存管理

Swift跟objective-c一样,使用ARC来管理内存。
ARC只针对类对象才生效,struct和enum都是值类型,它们的对象并不被ARC管理

处理对象reference cycle的三种方式

weak var name: Type

weak reference的特定,它只能被定义成var
weak reference用于解决成员允许为nil的reference cycle。

class Apartment {
    let unit: String
    weak var tenant: Person?

    // omit for simplicity...
}

unowned let name: Type

unowned reference用于解决成员不允许为nil的reference cycle
和strong reference相比,unowned reference只有一个特别:不会引起对象引用计数的变化。

class Apartment {
    let unit: String
    weak var tenant: Person?
    unowned let owner: Person

    // Omit for simplicity...
}

implicitly unwrapped optional

unowned reference和implicitly unwrapped optional配合在一起,用于解决引起reference cycle的两个成员都不允许为nil的情况。

class Country {
    let name: String
    // Implicitly Unwrapped Optional
    var capital: City! // default to nil
    
    init(name: String, capitalName: String) {
        self.name = name
        // Syntax Error!!!
        self.capital = City(name: capitalName, country: self)
    }
}

unowned var cn: Country? = Country(name: "China", capitalName: "Beijing")
var bj: City? = City(name: "Beijing", country: cn!)

处理closure和类对象之间的reference cycle

Swift无法确认当我们在Closure中使用self时,它已经被完整的初始化过了。如果我们需要这种初始化约束,我们可以把asHTML定义为lazy。

class HTMLElment {
    let name: String
    let text: String?
    var asHTML: Void -> String = { // WRONG SYNTAX!!!
        if let text = self.text {
            return "<\(self.name)>\(self.text)</\(self.name)>"
        }
        else {
            return "<\(self.name)>"
        }
    }

    // Omit for simplicity...
}

lazy可以确保一个成员只在类对象被完整初始化过之后,才能使用。

class HTMLElment {
    // Omit for simplicity
    lazy var asHTML: Void -> String = {
     // Omit for simplicity...
    }

    // Omit for simplicity...
}
  • 用capture list解决reference cycle

本质上来说,closure作为一个引用类型,解决reference cycle的方式和解决类对象之间的reference cycle是一样的,如果引起reference cycle的"捕获"不能为nil,就把它定义为unowned,否则,定义为weak。而指定“捕获”方式的地方,叫做closure的capture list。
使用一对 [] 表示closure的capture list

class HTMLElment {
    let name: String
    let text: String?
    lazy var asHTML: Void -> String = {
        // text
        // Capture list
        [unowned self] in
        if let text = self.text {
            return "<\(self.name)>\(self.text)</\(self.name)>"
        }
        else {
            return "<\(self.name)>"
        }
    }

    // Omit for simplicity...
}

如果closure带有完整的类型描述,capture list必须写在参数列表前面;
如果我们要在capture list里添加多个成员,用逗号把它们分隔开;

class HTMLElment {
    let name: String
    let text: String?
    lazy var asHTML: Void -> String = {
        // text
        // Capture list
        [unowned self /*, other capture member*/] () -> String in
        if let text = self.text {
            return "<\(self.name)>\(self.text)</\(self.name)>"
        }
        else {
            return "<\(self.name)>"
        }
    }

    // Omit for simplicity...
}

相关文章

网友评论

      本文标题:Swift3.0 总结

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