From: AceVest Date: Sun, 19 Mar 2017 12:51:11 +0000 (+0800) Subject: Swift - ErrorHandling TypeCasting NestedTypes Extensions Protocols X-Git-Url: http://zhaoyanbai.com/repos/dig.html?a=commitdiff_plain;h=4baee31a391ea0707b25952626dc087139ea8855;p=acecode.git Swift - ErrorHandling TypeCasting NestedTypes Extensions Protocols --- diff --git a/learn/AcePlay/AcePlay.playground/Pages/ErrorHandling.xcplaygroundpage/Contents.swift b/learn/AcePlay/AcePlay.playground/Pages/ErrorHandling.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..47bf506 --- /dev/null +++ b/learn/AcePlay/AcePlay.playground/Pages/ErrorHandling.xcplaygroundpage/Contents.swift @@ -0,0 +1,150 @@ +//: [Previous](@previous) + +import Foundation + +var str = "Hello, Error Handling" + +//: [Next](@next) + + +// 错误用符合Error协议的类型的值来表示。这个空协议表明该类型可以用于错误处理 + +enum VendingMachineError: Error { + case invalidSelection + case outOfSock + case insufficientFunds(coinsNeeded: Int) +} + + + +struct Item { + var price: Int + var count: Int +} + +class VendingMachine { + var inventory = [ + "Candy Bar": Item(price: 5, count: 100), + "Chips": Item(price: 9, count: 9), + "Pretzels" :Item(price: 7, count: 11) + ] + + var coinsDeposited = 0 + + func dispenseSnack(snack: String) { + print("Dispensing \(snack)") + } + + func vend(itemNamed name: String) throws { + guard let item = inventory[name] else { + throw VendingMachineError.invalidSelection + } + + guard item.count > 0 else { + throw VendingMachineError.outOfSock + } + + guard item.price <= coinsDeposited else { + throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited) + } + + coinsDeposited -= item.price + + inventory[name]!.count -= 1 + print("Dispensing \(name)") + } +} + + +let favoriteSnack = [ + "Alice": "Chips", + "Bob": "Licorice", + "Eve": "Pretzels" +] + + + +func buyFavoriteSnack(name: String, vendMachine: VendingMachine) throws { + let snackName = favoriteSnack[name] ?? "Candy Bar" + try vendMachine.vend(itemNamed: snackName) // 因为vend会抛出异常,所以前面加try关键字 +} + + +struct PurchasedSnack { + let name: String + + init(name: String, vendingMachine: VendingMachine) throws { // 构造器也可抛出异常 + try vendingMachine.vend(itemNamed: name) + self.name = name + } +} + + + +var vendingMachine = VendingMachine() +vendingMachine.coinsDeposited = 3 +do { + try buyFavoriteSnack(name: "Alice", vendMachine: vendingMachine) +} catch VendingMachineError.invalidSelection { + print("Invalid Selection") +} catch VendingMachineError.outOfSock { + print("Out of sock") +} catch VendingMachineError.insufficientFunds(let coinsNeeded) { + print("Insufficient funds. Please insert an additional \(coinsNeeded) coins") +} + + +// 将错误转换成可选值 +// 可以使用try?通过将错误转换成一个可选值来处理错误。如果在评估try?表达式时一个错误被抛出,那么表达式的值就是nil +//func someThrowingFunction() throws -> Int { +// // ... +//} +// +//let x = try? someThrowingFunction() +// +//let y: Int? +//do { +// y = try someThrowingFunction() +//} catch { +// y = nil +//} +// 如果someThrowingFunction()抛出一个错误,x和y的值是nil。否则x和y的值就是该函数的返回值。 +// 注意,无论someThrowingFunction()的返回值类型是什么类型,x和y都是这个类型的可选类型。例子中此函数返回一个整型,所以x和y是可选整型。 + + +// 禁用错误传递 +// 有时知道某个throwing函数实际上在运行时是不会抛出错误的,可以在表达式前面写try!来禁用错误传递 +// 这会把调用包装在一个不会有错误抛出的运行时断言中 +// 如果真的抛出了错误,会得到一个运行时错误 + + + + +// 指定清理操作 +func testDefer() { + print("\(#function):\(#line)") + defer { + print("Top Level Defer") + } + + print("\(#function):\(#line)") + if arc4random_uniform(100) < 50 { + print("\(#function):\(#line)") + defer { + print("Sub Level if Defer") + } + print("\(#function):\(#line)") + } else { + print("\(#function):\(#line)") + defer { + print("Sbu Level else Defer") + } + print("\(#function):\(#line)") + } + + print("\(#function):\(#line)") + print("End of Function \(#function)") +} + + +testDefer() diff --git a/learn/AcePlay/AcePlay.playground/Pages/Extensions.xcplaygroundpage/Contents.swift b/learn/AcePlay/AcePlay.playground/Pages/Extensions.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..4e753f7 --- /dev/null +++ b/learn/AcePlay/AcePlay.playground/Pages/Extensions.xcplaygroundpage/Contents.swift @@ -0,0 +1,188 @@ +//: [Previous](@previous) + +import Foundation + +var str = "Hello, Extensions" + +//: [Next](@next) + + +// 扩展 就是为一个已有的类、结构体、枚举类型或者协议类型添加新功能。这包括在没有权限获取原始源代码的情况下扩展类型的能力 + +// Swift 中的扩展可以: +// 1. 添加计算型属性和计算型类型属性 +// 2. 定义实例方法和类型方法 +// 3. 提供新的构造器 +// 4. 定义下标 +// 5. 定义和使用新的嵌套类型 +// 6. 使一个已有类型符合某个协议 + + +// 在 Swift 中,你甚至可以对协议进行扩展,提供协议要求的实现,或者添加额外的功能,从而可以让符合协议的类型拥有这些功能 +// 注意:扩展可以为一个类型添加新的功能,但是不能重写已有的功能。 + +// 语法 +// +//extension SomeType { +// somecode +//} + +// +//可以通过扩展来扩展一个已有类型,使其采纳一个或多个协议。在这种情况下,无论是类还是结构体,协议名字的书写方式完全一样: +// +//extension SomeType: SomeProtocol, AnotherProctocol { +// // 协议实现写到这里 +//} + +// 注意: 如果你通过扩展为一个已有类型添加新功能,那么新功能对该类型的所有已有实例都是可用的,即使它们是在这个扩展定义之前创建的 + + + +// 扩展计算型属性 +// 转换成米 +extension Double { + var km: Double { return self*1000 } + var m: Double { return self } + var cm: Double { return self/100 } + var ft: Double { return self/3.28084 } +} + +var tenThousandFeet = 10_000.ft +print("Ten thousand feets is \(tenThousandFeet) meters") + +let aMarathon = 42.km + 195.m +print("a marathon is \(aMarathon) meters long") + + +// 注意: 扩展可以添加新的计算型属性,但是不可以添加存储型属性,也不可以为已有属性添加属性观察器 + + + +// 扩展构造器 +// 扩展能为类添加新的便利构造器,但是它们不能为类添加新的指定构造器或析构器。指定构造器和析构器必须总是由原始的类实现来提供 +// 如果使用扩展为一个值类型添加构造器,同时该值类型的原始实现中未定义任何定制的构造器且所有存储属性提供了默认值,那么就可以在扩展中的构造器里调用默认构造器和逐一成员构造器。 +struct Size { + var width: Double = 0.0 + var height: Double = 0.0 +} + +struct Point { + var x: Double = 0.0 + var y: Double = 0.0 +} + + +struct Rect { + var origin = Point() + var size = Size() +} + + +var defaultRect = Rect() +var originRect = Rect(origin: Point(x: 2.0, y:2.0), size: Size(width: 5.0, height: 5.0)) + + +// 可以为Rect扩展一个指定中心点和Size的构造器 +extension Rect { + init(center: Point, size: Size) { + let originX = center.x - size.width / 2 + let originY = center.y - size.height / 2 + self.init(origin: Point(x: originX, y: originY), size: size) + } +} + +var centerRect = Rect(center: Point(x: 0.0, y: 0.0), size: Size(width: 5.0, height: 5.0)) + + +// 扩展方法 + +extension Int { + func repeatTask(task: (Int) -> Void) { + for i in 0.. Int { + self = self * self + return self + } +} + +var someInt = 3 +print(someInt.square()) +print(someInt) + + +// 扩展下标 +extension Int { + subscript(index: Int) -> Int { + + var base = 1 + + for _ in 1.. 0: + return .Positive + case Int.min..<0 : + return .Negative + default: + return .Zero + } + } +} + + +func printIntegerKinds(_ numbers: [Int]) { + for n in numbers { + var s: String = "" + switch n.kind { + case .Negative: + s += "-" + case .Zero: + s += " " + case .Positive: + s += "+" + } + + s += "\(n.kind)" + + print(s) + } +} + + +var numbers: [Int] = [1, 2, 3, -1, 0, -8] +printIntegerKinds(numbers) \ No newline at end of file diff --git a/learn/AcePlay/AcePlay.playground/Pages/NestedTypes.xcplaygroundpage/Contents.swift b/learn/AcePlay/AcePlay.playground/Pages/NestedTypes.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..22309b0 --- /dev/null +++ b/learn/AcePlay/AcePlay.playground/Pages/NestedTypes.xcplaygroundpage/Contents.swift @@ -0,0 +1,54 @@ +//: [Previous](@previous) + +import Foundation + +var str = "Hello, NestTypes" + +//: [Next](@next) + + +struct BlackjackCard { + // 嵌套的 Suit 枚举 + enum Suit: Character { + case Spades = "♠", Hearts = "♡", Diamonds = "♢", Clubs = "♣" + } + + // 嵌套的 Rank 枚举 + enum Rank: Int { + case Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten + case Jack, Queen, King, Ace + struct Values { + let first: Int, second: Int? + } + var values: Values { + switch self { + case .Ace: + return Values(first: 1, second: 11) + case .Jack, .Queen, .King: + return Values(first: 10, second: nil) + default: + return Values(first: self.rawValue, second: nil) + } + } + } + + // BlackjackCard 的属性和方法 + let rank: Rank, suit: Suit + var description: String { + var output = "suit is \(suit.rawValue)," + output += " value is \(rank.values.first)" + if let second = rank.values.second { + output += " or \(second)" + } + return output + } +} + +let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades) +print("theAceOfSpades: \(theAceOfSpades.description)") + + +// 引用嵌套类型 +// 在外部引用嵌套类型时,在嵌套类型的类型名前加上其外部类型的类型名作为前缀 +let heartsSymbol = BlackjackCard.Suit.Hearts.rawValue +print(heartsSymbol) diff --git a/learn/AcePlay/AcePlay.playground/Pages/Protocols.xcplaygroundpage/Contents.swift b/learn/AcePlay/AcePlay.playground/Pages/Protocols.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..fa880fd --- /dev/null +++ b/learn/AcePlay/AcePlay.playground/Pages/Protocols.xcplaygroundpage/Contents.swift @@ -0,0 +1,108 @@ +//: [Previous](@previous) + +import Foundation + +var str = "Hello, Protocols" + +//: [Next](@next) + +//协议语法 +//协议的定义方式与类、结构体和枚举的定义非常相似: +// +//protocol SomeProtocol { +// // 这里是协议的定义部分 +//} +//要让自定义类型遵循某个协议,在定义类型时,需要在类型名称后加上协议名称,中间以冒号(:)分隔。遵循多个协议时,各协议之间用逗号(,)分隔: +// +//struct SomeStructure: FirstProtocol, AnotherProtocol { +// // 这里是结构体的定义部分 +//} +//拥有父类的类在遵循协议时,应该将父类名放在协议名之前,以逗号分隔: +// +//class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol { +// // 这里是类的定义部分 +//} + + +//协议可以要求遵循协议的类型提供特定名称和类型的实例属性或类型属性。协议不指定属性是存储型属性还是计算型属性,它只指定属性的名称和类型。此外,协议还指定属性是可读的还是可读可写的。 +// +//如果协议要求属性是可读可写的,那么该属性不能是常量属性或只读的计算型属性。如果协议只要求属性是可读的,那么该属性不仅可以是可读的,如果代码需要的话,还可以是可写的。 +// +//协议总是用 var 关键字来声明变量属性,在类型声明后加上 { set get } 来表示属性是可读可写的,可读属性则用 { get } 来表示: +// +//protocol SomeProtocol { +// var mustBeSettable: Int { get set } +// var doesNotNeedToBeSettable: Int { get } +//} +//在协议中定义类型属性时,总是使用 static 关键字作为前缀。当类类型遵循协议时,除了 static 关键字,还可以使用 class 关键字来声明类型属性: +// +//protocol AnotherProtocol { +// static var someTypeProperty: Int { get set } +//} + + + +protocol FullNamed { + var fullName: String { get } +} + +struct Person: FullNamed { + var fullName: String +} + +let john = Person(fullName: "John") + + +class Starship: FullNamed { + var name: String + var prefix: String? + + init(name: String, prefix: String? = nil) { + self.name = name + self.prefix = prefix + } + + var fullName: String { + return "\(prefix ?? "") \(name)" + } +} + +var ncc1701 = Starship(name: "Enterprise", prefix: "USS") +print(ncc1701.fullName) + + + + +// 方法要求 +// 协议可以要求遵循协议的类型实现某些指定的实例方法或类方法。这些方法作为协议的一部分,像普通方法一样放在协议的定义中,但是不需要大括号和方法体。 +// 可以在协议中定义具有可变参数的方法,和普通方法的定义方式相同。但是,不支持为协议中的方法的参数提供默认值。 + + +//正如属性要求中所述,在协议中定义类方法的时候,总是使用 static 关键字作为前缀。当类类型遵循协议时,除了 static 关键字,还可以使用 class 关键字作为前缀: +// +//protocol SomeProtocol { +// static func someTypeMethod() +//} + + +protocol RandomNumberGenerator { + func random() -> Double +} + + +class LinearCongruentialGenerator: RandomNumberGenerator { + var lastRandom = 42.0 + let m = 139968.0 + let a = 3877.0 + let c = 29573.0 + + func random() -> Double { + lastRandom = ((lastRandom * a + c).truncatingRemainder(dividingBy:m)) + return lastRandom / m + } +} + +let generator = LinearCongruentialGenerator() +print("Here is a random number: \(generator.random())") +print("Here is another random number: \(generator.random())") + diff --git a/learn/AcePlay/AcePlay.playground/Pages/TypeCasting.xcplaygroundpage/Contents.swift b/learn/AcePlay/AcePlay.playground/Pages/TypeCasting.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..15f907b --- /dev/null +++ b/learn/AcePlay/AcePlay.playground/Pages/TypeCasting.xcplaygroundpage/Contents.swift @@ -0,0 +1,129 @@ +//: [Previous](@previous) + +import Foundation + +var str = "Hello, TypeCasting" + +//: [Next](@next) + + +class MediaItem { + var name: String + + init(name: String) { + self.name = name + } +} + + +class Movie: MediaItem { + var director: String + init(name: String, director: String) { + self.director = director + + super.init(name: name) + } +} + +class Song: MediaItem { + var artist: String + init(name: String, artist: String) { + self.artist = artist + + super.init(name: name) + } +} + + +// 这个数组将会被推断为MediaItem类型 +let library = [ + Movie(name: "Casablanca", director: "Michael Curtiz"), + Song(name: "Blue Suede Shoes", artist: "Elvis Presley"), + Movie(name: "Citizen Kane", director: "Orson Welles"), + Song(name: "The One And Only", artist: "Chesney Hawkes"), + Song(name: "Never Gonna Give You Up", artist: "Rick Astley") +] + + + +var movieCount = 0 +var songCount = 0 + +for i in library { + if i is Movie { + movieCount += 1 + } else if i is Song { + songCount += 1 + } +} + +print("Media Library contains \(movieCount) Movies and \(songCount) Songs") +print("------------------------------------------------------------------") + + + +// 向下转型 +// 某类型的一个常量或变量可能在幕后实际上属于一个子类。当确定是这种情况时,你可以尝试向下转到它的子类型,用类型转换操作符(as? 或 as!) +// 因为向下转型可能会失败,类型转型操作符带有两种不同形式。条件形式as? 返回一个你试图向下转成的类型的可选值。强制形式 as! 把试图向下转型和强制解包转换结果结合为一个操作 + +for m in library { + if let movie = m as? Movie { + print("Movie: \(movie.name) directed by \(movie.director)") + } else if let song = m as? Song { + print("Song: \(song.name) by \(song.artist)") + } +} + + + + + +// Any和AnyObject的类型转换 +// Swift为不确定类型提供了两种特殊的类型别名 +// 1. Any可以表示任何类型,包括函数类型 +// 2. AnyObject可以表示任何类类型的实例 + +var things = [Any]() +things.append(99) +things.append(0) +things.append(2.718281828459) +things.append(0.0) +things.append("Hello Swift") +things.append((3, 4)) +things.append(Movie(name: "Citizen Kane", director: "Orson Welles")) +things.append( { (name: String) -> String in "Hello \(name)!" } ) + + +print("------------------------------------------------------------------") + +for t in things { + switch t { + case 0 as Int: + print("zero as Int") + case 0 as Double: + print("zero as Double") + case let someInt as Int : + print("an integer value of \(someInt)") + case let someDouble as Double where someDouble > 0 : + print("a positive double value of \(someDouble)") + case is Double: + print("some other double value that i do not want to print") + case let someStr as String: + print("a string value of \"\(someStr)\"") + case let (x, y) as (Int, Int) : + print("an (x, y) point at (\(x), \(y)") + case let movie as Movie: + print("a movie named \(movie.name) directed by \(movie.director)") + case let strConverter as (String) -> String : + print(strConverter("Ace")) + default: + print("Something else") + } +} + + +// 注意:Any类型可以表示所有类型的值,包括可选类型 +// Swift会在你用Any类型来表示一个可选值的时候,给一个警告。如果确实想使用Any类型来承载可选值,你可以使用as操作符显式转换为Any +let optionalNumber: Int? = 3 +// things.append(optionalNumber) // 会有warning +things.append(optionalNumber as Any) // 不会有warning diff --git a/learn/AcePlay/AcePlay.playground/contents.xcplayground b/learn/AcePlay/AcePlay.playground/contents.xcplayground index bc794cb..e710ae5 100644 --- a/learn/AcePlay/AcePlay.playground/contents.xcplayground +++ b/learn/AcePlay/AcePlay.playground/contents.xcplayground @@ -16,5 +16,10 @@ + + + + + \ No newline at end of file diff --git a/learn/AcePlay/AcePlay.playground/playground.xcworkspace/xcuserdata/Ace.xcuserdatad/UserInterfaceState.xcuserstate b/learn/AcePlay/AcePlay.playground/playground.xcworkspace/xcuserdata/Ace.xcuserdatad/UserInterfaceState.xcuserstate index c4b2fc5..5ea7ff6 100644 Binary files a/learn/AcePlay/AcePlay.playground/playground.xcworkspace/xcuserdata/Ace.xcuserdatad/UserInterfaceState.xcuserstate and b/learn/AcePlay/AcePlay.playground/playground.xcworkspace/xcuserdata/Ace.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/learn/AcePlay/AcePlay.playground/playground.xcworkspace/xcuserdata/Ace.xcuserdatad/WorkspaceSettings.xcsettings b/learn/AcePlay/AcePlay.playground/playground.xcworkspace/xcuserdata/Ace.xcuserdatad/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..dd7403b --- /dev/null +++ b/learn/AcePlay/AcePlay.playground/playground.xcworkspace/xcuserdata/Ace.xcuserdatad/WorkspaceSettings.xcsettings @@ -0,0 +1,16 @@ + + + + + BuildLocationStyle + UseAppPreferences + CustomBuildLocationType + RelativeToDerivedData + DerivedDataLocationStyle + Default + IssueFilterStyle + ShowActiveSchemeOnly + LiveSourceIssuesEnabled + + +