Swift5
import UIKit
var str = "Hello, playground"//介绍swift5相关的语法//********************常量和变量/* 1.常量定义时必须赋初值, 2.变量定义时必须加 “!” or “?” 3.可以在一行中声明多个常量或者多个变量,用逗号隔开: */let maximumNumberOfLoginAttempts = 10var currentLoginAttempt = 0var x = 0.0, y = 0.0, z = 0.0var sgfdfd:Int?var welcomeMessage: String!var welcomeMessageInt: Int?var optionalString: String? = "Hello"print(optionalString == nil)// Prints "false"var optionalName: String? = "John Appleseed"var greeting = "Hello!"if let name = optionalName { greeting = "Hello, \(name)"}//********************条件语句//Switchlet vegetable = "red pepper"switch vegetable { case "celery": print("Add some raisins and make ants on a log.")case "cucumber", "watercress": print("That would make a good tea sandwich.")case let x where x.hasSuffix("pepper"): print("Is it a spicy \(x)?")default: print("Everything tastes good in soup.")}// Prints "Is it a spicy red pepper?"//for-in//字典是无序的!!!let interestingNumbers = [ "Prime": [2, 3, 5, 7, 11, 13], "Fibonacci": [1, 1, 2, 3, 5, 8], "Square": [1, 4, 9, 16, 25],]var largest = 0for (kind, numbers) in interestingNumbers { print(kind) for number in numbers { if number > largest { largest = number } }}print(largest)//while-repeatvar n = 2while n < 100 { n *= 2}print(n)// Prints "128"var m = 2repeat { m *= 2} while m < 100print(m)// Prints "128"//You can keep an index in a loop by using ..< to make a range of indexes.var total = 0for i in 0..<4 { total += i}print(total)//********************数组和字典//创建方式一var shoppingList = ["catfish", "water", "tulips"]shoppingList[1] = "bottle of water"var occupations = [ "Malcolm": "Captain", "Kaylee": "Mechanic",]occupations["Jayne"] = "Public Relations"shoppingList.append("blue paint")print(shoppingList)//创建方式二let emptyArray = [String]()let emptyDictionary = [String: Float]()let individualScores = [75, 43, 103, 87, 12]//注意if 语句的使用var teamScore = 0for score in individualScores { if score > 50 { teamScore += 3 } else { teamScore += 1 }}print(teamScore)// Prints "11"//********************Functions and Closures/* 函数实际上是闭包的一种特殊情况: 方法名可以一样,但是方法里的参数设置不能一样,否则报错 *////简单方法func greet(person: String, day: String) -> String { return "Hello \(person), today is \(day)."}func greet(_ person: String, on day: String) -> String { return "Hello \(person), today is \(day)."}func greet(_ person: String, ooon day: String) -> Int { return 15}greet(person: "Bob", day: "Tuesday")greet("John", on: "Wednesday")greet("Bill", ooon: "TOne")///返回多个参数的方法[通过tuple 实现]func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) { var min = scores[0] var max = scores[0] var sum = 0 for score in scores { if score > max { max = score } else if score < min { min = score } sum += score } return (min, max, sum)}let statistics = calculateStatistics(scores: [5, 3, 100, 3, 9])print(statistics.sum)// Prints "120"print(statistics.2)// Prints "120"///函数可以嵌套。嵌套函数可以访问在外部函数中声明的变量。可以使用嵌套函数在长函数或复杂函数中组织代码。func returnFifteen() -> Int { var y = 10 func add() { y += 5 } add() return y}returnFifteen()///函数可以返回另一个函数作为它的值。func makeIncrementer() -> ((Int) -> Int) { func addOne(number: Int) -> Int { return 1 + number } return addOne}var increment = makeIncrementer()increment(7)///一个函数可以接受另一个函数作为它的参数之一。func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool { for item in list { if condition(item) { return true } } return false}func lessThanTen(number: Int) -> Bool { return number < 10}var numbers = [20, 19, 7, 12]hasAnyMatches(list: numbers, condition: lessThanTen)///函数实际上是闭包的一种特殊情况:numbers.map({ (number: Int) -> Int in let result = 3 * number return result})///您有几个选项可以更简洁地编写闭包。当闭包的类型已知时,例如委托的回调,您可以省略它的参数类型、返回类型,或者两者都省略。单个语句闭包隐式地返回它们唯一语句的值。let mappedNumbers = numbers.map({ number in 3 * number })print(mappedNumbers)// Prints "[60, 57, 21, 36]"//********************Objects and Classes//一个简单的类class Shape { var numberOfSides = 0 func simpleDescription() -> String { return "A shape with \(numberOfSides) sides." }}var shape = Shape()shape.numberOfSides = 7var shapeDescription = shape.simpleDescription()//一个有init 的类class NamedShape { var numberOfSides: Int = 0 var name: String init(name: String) { self.name = name } func simpleDescription() -> String { return "A shape with \(numberOfSides) sides." } }//实现继承的类class Square: NamedShape { var sideLength: Double init(sideLength: Double, name: String) { self.sideLength = sideLength super.init(name: name) numberOfSides = 4 } func area() -> Double { return sideLength * sideLength } override func simpleDescription() -> String { return "A square with sides of length \(sideLength)." }}let test = Square(sideLength: 5.2, name: "my test square")test.area()test.simpleDescription()///除了存储的简单属性外,属性还可以有getter和setter。class EquilateralTriangle: NamedShape { var sideLength: Double = 0.0 init(sideLength: Double, name: String) { self.sideLength = sideLength super.init(name: name) numberOfSides = 3 } var perimeter: Double { get { return 3.0 * sideLength } set { sideLength = newValue / 3.0 } } override func simpleDescription() -> String { return "An equilateral triangle with sides of length \(sideLength)." }}var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")print(triangle.perimeter)// Prints "9.3"triangle.perimeter = 9.9print(triangle.sideLength)// Prints "3.3000000000000003"///如果您不需要计算属性,但仍然需要提供在设置新值之前和之后运行的代码,那么使用willSet和didSet。只要值在初始化器之外发生更改,就会运行您提供的代码。例如,下面的类确保三角形的边长总是与其正方形的边长相同。class TriangleAndSquare { var triangle: EquilateralTriangle { willSet { square.sideLength = newValue.sideLength } } var square: Square { willSet { triangle.sideLength = newValue.sideLength } } init(size: Double, name: String) { square = Square(sideLength: size, name: name) triangle = EquilateralTriangle(sideLength: size, name: name) }}var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")print(triangleAndSquare.square.sideLength)// Prints "10.0"print(triangleAndSquare.triangle.sideLength)// Prints "10.0"triangleAndSquare.square = Square(sideLength: 50, name: "larger square")print(triangleAndSquare.triangle.sideLength)// Prints "50.0"///当处理可选值时,您可以编写?在方法、属性和下标等操作之前。如果之前的值是?是nil吗?被忽略,整个表达式的值为nil。否则,可选值将被打开,并且在?作用于打开的值。在这两种情况下,整个表达式的值都是一个可选值。let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")let sideLength = optionalSquare?.sideLength//********************Enumerations and Structures//使用枚举创建枚举。与类和所有其他命名类型一样,枚举可以具有与其关联的方法。enum Rank: Int { case ace = 1 case two, three, four, five, six, seven, eight, nine, ten case jack, queen, king func simpleDescription() -> String { switch self { case .ace: return "ace" case .jack: return "jack" case .queen: return "queen" case .king: return "king" default: return String(self.rawValue) } }}let ace = Rank.acelet aceRawValue = ace.rawValue//使用init?(rawValue:)初始化器从原始值生成枚举的实例。如果没有匹配的秩,则返回匹配原始值的枚举用例或nil。if let convertedRank = Rank(rawValue: 3) { let threeDescription = convertedRank.simpleDescription()}//枚举的大小写值是实际值,而不仅仅是编写原始值的另一种方式。事实上,在没有有意义的原始值的情况下,您不需要提供一个。enum Suit { case spades, hearts, diamonds, clubs func simpleDescription() -> String { switch self { case .spades: return "spades" case .hearts: return "hearts" case .diamonds: return "diamonds" case .clubs: return "clubs" } }}let hearts = Suit.heartslet heartsDescription = hearts.simpleDescription()//如果枚举具有原始值,这些值将作为声明的一部分确定,这意味着特定枚举用例的每个实例始终具有相同的原始值。枚举用例的另一个选择是拥有与该用例相关联的值——这些值是在创建实例时确定的,并且对于枚举用例的每个实例,它们可以是不同的。可以将关联值视为枚举用例实例的存储属性。例如,考虑从服务器请求日出和日落时间的情况。服务器要么响应所请求的信息,要么响应错误的描述。enum ServerResponse { case result(String, String) case failure(String)}let success = ServerResponse.result("6:00 am", "8:09 pm")let failure = ServerResponse.failure("Out of cheese.")switch success { case let .result(sunrise, sunset): print("Sunrise is at \(sunrise) and sunset is at \(sunset).")case let .failure(message): print("Failure... \(message)")}// Prints "Sunrise is at 6:00 am and sunset is at 8:09 pm."//使用struct创建结构。结构支持许多与类相同的行为,包括方法和初始化器。结构和类之间最重要的区别之一是,结构在代码中传递时总是被复制,但是类是通过引用传递的。struct Card { var rank: Rank var suit: Suit func simpleDescription() -> String { return "The \(rank.simpleDescription()) of \(suit.simpleDescription())" }}let threeOfSpades = Card(rank: .three, suit: .spades)let threeOfSpadesDescription = threeOfSpades.simpleDescription()//********************Protocols and Extensions//使用协议声明协议。//使用 mutating 关键字修饰方法是为了能在该方法中修改 struct 或是 enum 的变量,在设计接口的时候,也要考虑到使用者程序的扩展性。所以要多考虑使用mutating来修饰方法。//注意,在SimpleStructure声明中使用mutating关键字来标记修改结构的方法。SimpleClass的声明不需要任何标记为mutating的方法,因为类上的方法总是可以修改该类。protocol ExampleProtocol { var simpleDescription: String { get } mutating func adjust()}class SimpleClass: ExampleProtocol { var simpleDescription: String = "A very simple class." var anotherProperty: Int = 69105 func adjust() { simpleDescription += " Now 100% adjusted." }}var a = SimpleClass()a.adjust()let aDescription = a.simpleDescriptionstruct SimpleStructure: ExampleProtocol { var simpleDescription: String = "A simple structure" mutating func adjust() { simpleDescription += " (adjusted)" }}var b = SimpleStructure()b.adjust()let bDescription = b.simpleDescription//使用扩展向现有类型添加功能,如新方法和计算属性。您可以使用扩展将协议一致性添加到在其他地方声明的类型,甚至添加到从库或框架导入的类型。extension Int: ExampleProtocol { var simpleDescription: String { return "The number \(self)" } mutating func adjust() { self += 42 }}print(7.simpleDescription)// Prints "The number 7"//您可以像使用任何其他命名类型一样使用协议名称—例如,创建具有不同类型但都符合单个协议的对象集合。当处理类型为协议类型的值时,不能使用协议定义之外的方法。let protocolValue: ExampleProtocol = aprint(protocolValue.simpleDescription)// Prints "A very simple class. Now 100% adjusted."// print(protocolValue.anotherProperty) // Uncomment to see the error//即使变量protocolValue有一个SimpleClass的运行时类型,编译器仍然将它视为给定类型的ExampleProtocol。这意味着除了协议一致性之外,您不能意外地访问类实现的方法或属性。//********************Error Handling//使用采用错误协议的任何类型表示错误。enum PrinterError: Error { case outOfPaper case noToner case onFire}//使用throw来抛出错误,并使用throw来标记可能抛出错误的函数。如果在函数中抛出错误,函数将立即返回,调用该函数的代码将处理该错误。func send(job: Int, toPrinter printerName: String) throws -> String { if printerName == "Never Has Toner" { throw PrinterError.noToner } return "Job sent"}//有几种方法可以处理错误。一种方法是使用do-catch。在do块内部,您可以通过在其前面编写try来标记可能抛出错误的代码。在catch块内部,除非您给它一个不同的名称,否则错误会自动给出名称错误。do { let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng") print(printerResponse)} catch { print(error)}// Prints "Job sent"//您可以提供多个catch块来处理特定的错误。在catch之后编写模式,就像在开关的case之后一样。do { let printerResponse = try send(job: 1440, toPrinter: "Gutenberg") print(printerResponse)} catch PrinterError.onFire { print("I'll just put this over here, with the rest of the fire.")} catch let printerError as PrinterError { print("Printer error: \(printerError).")} catch { print(error)}// Prints "Job sent"//另一种处理错误的方法是使用try?将结果转换为可选的。如果函数抛出错误,则丢弃特定的错误,结果为nil。否则,结果是一个可选的,包含函数返回的值。let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner")//使用defer编写一个代码块,该代码块在函数中所有其他代码之后,即函数返回之前执行。无论函数是否抛出错误,都会执行代码。您可以使用defer互相编写安装和清理代码,即使它们需要在不同的时间执行。var fridgeIsOpen = falselet fridgeContent = ["milk", "eggs", "leftovers"]func fridgeContains(_ food: String) -> Bool { fridgeIsOpen = true defer { fridgeIsOpen = false } let result = fridgeContent.contains(food) return result}fridgeContains("banana")print(fridgeIsOpen)// Prints "false"//********************泛型//在尖括号内编写一个名称,使其成为泛型函数或类型。func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] { var result = [Item]() for _ in 0..<numberOfTimes { result.append(item) } return result}makeArray(repeating: "knock", numberOfTimes: 4)//您可以创建函数和方法的通用形式,以及类、枚举和结构。// Reimplement the Swift standard library's optional typeenum OptionalValue<Wrapped> { case none case some(Wrapped)}var possibleInteger: OptionalValue<Int> = .nonepossibleInteger = .some(100)//在正文前面指定需求列表的位置使用—例如,要求类型实现协议,要求两种类型相同,或者要求类具有特定的超类。//sequence 是一个协议//在 Swift 4 中可以在 associatedtype 后面声明的类型后追加 where 语句func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool where T.Element: Equatable, T.Element == U.Element{ for lhsItem in lhs { for rhsItem in rhs { if lhsItem == rhsItem { return true } } } return false}anyCommonElements([1, 2, 3], [3])//上面这个假定函数有两个类型参数。第一个类型参数T,有一个需要T必须遵循Sequence协议的类型约束;第二个类型参数U,有一个需要U必须遵循Sequence协议的类型约束。print("end")