• Swift语法入门


    正文参考:

    https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/GuidedTour.html#//apple_ref/doc/uid/TP40014097-CH2-ID1

    ------------------------------------2016/07/01写在前面的分割线---------------------------------------------------------------

    距2015/9/10,时隔几个月之后重新回来捡起Swift,它尽然已经更新到3.0preview版本了。辞旧迎新,决定在旧博文添加新内容,必要的地方会标注出不同版本的差异。

    中文翻译:来自苹果Swift.org的官方推荐,可以一看,the-swift-programming-language-in-chinese

    开发者网站上的语法更新较慢,也可能是release之后才会更新。

    要看最新的包括preview版本的Swift语法,可以下载epub版本的语法电子书,链接在:https://swift.org/documentation/#the-swift-programming-language

    ------------------------------------------正文--------------------------------------------------------------------------------------------

    一、简单数值类型

    1、let关键字声明一个常量,var关键字声明一个变量,每条语句后面可以不接分号。

    var myVariable = 42
    myVariable = 50
    let myConstant = 42

    2、常量或变量的数据类型不必显示声明,初始化赋值时编译器会推断数据类型。当然,也可以显示声明数据类型。

    let implicitInteger = 70
    let implicitDouble = 70.0
    let explicitDouble: Double = 70

    3、不同类型数据之间不存在隐式转换,如果需要转换,则需要进行显示声明。

    let label = "The width is "
    let width = 94
    let widthLabel = label + String(width)

    4、使用()可以将数值插入string变量中

    let apples = 3
    let oranges = 5
    let appleSummary = "I have (apples) apples."
    let fruitSummary = "I have (apples + oranges) pieces of fruit."

    5、用[]创建array类型和dictionary类型的数据,可以用index或key作为索引,最后一个元素后面允许接一个逗号。

    var shoppingList = ["catfish", "water", "tulips", "blue paint"]
    shoppingList[1] = "bottle of water"
     
    var occupations = [
        "Malcolm": "Captain",
        "Kaylee": "Mechanic",
    ]
    occupations["Jayne"] = "Public Relations"

    6、声明一个空的array或dictionary

    let emptyArray = [String]()
    let emptyDictionary = [String: Float]()

    如果元素类型已知,也可以如下赋空值:

    shoppingList = []
    occupations = [:]

    二、控制流

    1、if和switch进行条件控制,for-inforwhile, and 和repeat-while进行循环控制

    条件语句可以不加括号,但控制段的{}是必须的

    条件语句的值必须是Boolean类型,不存在其他类型与Boolean类型的隐式转换。

    let individualScores = [75, 43, 103, 87, 12]
    var teamScore = 0
    for score in individualScores {
        if score > 50 {
            teamScore += 3
        } else {
            teamScore += 1
        }
    }
    print(teamScore)

    2、可以用if和let来处理值可能为空的情况,这些值是optional类型,在声明数据类型后面加一个问号,就声明了一个optional类型的值,其可能有值,也可能为nil。

    var optionalString: String? = "Hello"
    print(optionalString == nil)
     
    var optionalName: String? = "John Appleseed"
    var greeting = "Hello!"
    if let name = optionalName {
        greeting = "Hello, (name)"
    }

    如果optionalName的值为nil,则判断语句的值为false,否则,判断语句为真,optionalName的unwrapped值将赋值给name变量。

    另一种处理optinal值的方式是用??运算符来提供默认值。若oprinal值为nil,则赋默认值。

    let nickName: String? = nil
    let fullName: String = "John Appleseed"
    let informalGreeting = "Hi (nickName ?? fullName)"

    如上,nickName为nil, 则字符串中取fullName的值。
    3、switch支持多种数据类型和多种比较操作,并不局限于整数类型

    let 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.")
    }

    注意这里的let以一定格式来为x赋值。

    switch中,执行完某一个case中的语句后不再继续执行下面case,所以不必显示的添加break语句。

    4、for-in中可以使用键值对来对dictionary进行迭代,由于dictionary是乱序的,所以迭代顺序是任意的。

    let interestingNumbers = [
        "Prime": [2, 3, 5, 7, 11, 13],
        "Fibonacci": [1, 1, 2, 3, 5, 8],
        "Square": [1, 4, 9, 16, 25],
    ]
    var largest = 0
    for (kind, numbers) in interestingNumbers {
        for number in numbers {
            if number > largest {
                largest = number
            }
        }
    }
    print(largest)

    5、while 和repeat-while

    var n = 2
    while n < 100 {
        n = n * 2
    }
    print(n)
     
    var m = 2
    repeat {
        m = m * 2
    } while m < 100
    print(m)

    6、可以用..<来表示循环条件的范围,也可以显示写出

    var firstForLoop = 0
    for i in 0..<4 {
        firstForLoop += i
    }
    print(firstForLoop)
     
    var secondForLoop = 0
    for var i = 0; i < 4; ++i {
        secondForLoop += i
    }
    print(secondForLoop)

    用..<表示一个半闭区间,用...表示一个闭合区间

    三、函数和闭包(Functions And Closures)

    1、用func关键字声明一个函数,括号中声明参数列表,用->声明函数返回类型

    func greet(name: String, day: String) -> String {
        return "Hello (name), today is (day)."
    }
    greet("Bob", day: "Tuesday")

    默认情况下,函数用参数名来作为参数的label。也可以在函数名前面写出自定义的label,或者写_表示不用label。

    func greet(_ person: String, on day: String) -> String {
        return "Hello (person), today is (day)."
    }
    greet("John", on: "Wednesday")

    (参数名是在函数内部实现中使用的名字,而label则是外部调用函数时显示的参数名字)

    2、用一个元组(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([5, 3, 100, 3, 9])
    print(statistics.sum)
    print(statistics.2)

    3、函数的参数可以是不定数目的,把他们放入一个array即可。

    func sumOf(numbers: Int...) -> Int {
        var sum = 0
        for number in numbers {
            sum += number
        }
        return sum
    }
    sumOf()
    sumOf(42, 597, 12)

    4、函数之间可以嵌套,可以使用内部函数来减小函数的复杂度和长度。

    func returnFifteen() -> Int {
        var y = 10
        func add() {
            y += 5
        }
        add()
        return y
    }
    returnFifteen()

    5、函数的返回值可以是一个函数。

    func makeIncrementer() -> (Int -> Int) {
        func addOne(number: Int) -> Int {
            return 1 + number
        }
        return addOne
    }
    var increment = makeIncrementer()
    increment(7)

    6、函数的参数可以是一个函数。

    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(numbers, condition: lessThanTen)

    7、函数实际上是一种特殊的闭包(代码块可以迟滞调用)。闭包中的代码可以访问其范围内的变量和函数,甚至是其他范围内。可以用{}直接声明一个没有名字的Closure,用in关键字来分开参数列表返回类型和函数主体。

    numbers.map({
        (number: Int) -> Int in
        let result = 3 * number
        return result
    })

    8、有一些技巧可以使闭包更为精简。

    如果闭包的类型已知,例如委托调用,你可以省略参数类型或返回类型,只有一条语句的闭包默认返回这条语句的值。

    let mappedNumbers = numbers.map({ number in 3 * number })
    print(mappedNumbers)

    可以用数字代替名字来声明参数,闭包作为函数的最后一个参数时可以立即显示在()后面,如果闭包作为函数的唯一参数,则()可以省略。

    let sortedNumbers = numbers.sort { $0 > $1 }
    print(sortedNumbers)

    四、对象和类

    1、创建一个类

    class Shape {
        var numberOfSides = 0
        func simpleDescription() -> String {
            return "A shape with (numberOfSides) sides."
        }
    }

    2、创建类的一个实例

    var shape = Shape()
    shape.numberOfSides = 7
    var shapeDescription = shape.simpleDescription()

    3、上面的类缺少初始化器,用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."
        }
    }

    每一个属性都要在声明时或初始化器中进行赋一个初值。

    4、使用deinit来清理一些当对象被清理时需要清理的变量。

    5、创建一个子类

    override声明子类中复写父类的方法。

    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()

    6、属性可以有setter和getter

    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)
    triangle.perimeter = 9.9
    print(triangle.sideLength)

    setter中默认传入的值的名字是newValue,可以在set后面用()声明一个自定义的名字。

    7、用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)
    print(triangleAndSquare.triangle.sideLength)
    triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
    print(triangleAndSquare.triangle.sideLength)

    9、处理可选值类型

    let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
    let sideLength = optionalSquare?.sideLength

    如果可选值为nil,则?后面的代码被忽略,整个表达式的值为nil。

    五、枚举和结构

    1、enum声明枚举类型,enum也可以有自己的函数

    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.Ace
    let aceRawValue = ace.rawValue

    2、用init?(rawValue:)来初始化一个枚举类型的实例

    if let convertedRank = Rank(rawValue: 3) {
        let threeDescription = convertedRank.simpleDescription()
    }

    3、一个枚举的枚举值就是它实际的值,而不仅仅是它的raw值的另一种写法。如果枚举没有意义性的raw类型,可以不提供raw类型(也就是数据类型)。

    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.Hearts
    let heartsDescription = hearts.simpleDescription()

    如果没有声明enum的数据类型,则Suit.Hearts将通过全称被引用给hearts变量。

    在使用swich时,枚举值可以省略枚举类型,按上面的例子,可直接用.hearts。

    4、struct声明一个结构类型

    struct和class类似,都有方法和初始化器。最重要的区别在于,赋值时struct是整个值复制过去,而class是增加引用。struct是值类型,而class是引用类型。

    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()

    5、Associated values和Raw Values

    同一个枚举值可能有不同的Associated value,当创建枚举实例时才提供Associated value。

    而对于同一个枚举的所有实例而言,它们的raw value都是一样的,当定义枚举时就提供了Raw Values.

    例如:

    (String,String)是success的Raw value,而("6:00 am", "8:09 pm")是success的Associated value。

    enum ServerResponse {
        case Result(String, String)
        case Error(String)
    }
     
    let success = ServerResponse.Result("6:00 am", "8:09 pm")
    let failure = ServerResponse.Error("Out of cheese.")
     
    switch success {
    case let .Result(sunrise, sunset):
        let serverResponse = "Sunrise is at (sunrise) and sunset is at (sunset)."
    case let .Error(error):
        let serverResponse = "Failure...  (error)"
    }

    六、协议和扩展

    1、protocol关键字声明一个协议

    protocol ExampleProtocol {
        var simpleDescription: String { get }
        mutating func adjust()
    }

    2、类、枚举和结构都可以实现协议

    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.simpleDescription
     
    struct SimpleStructure: ExampleProtocol {
        var simpleDescription: String = "A simple structure"
        mutating func adjust() {
            simpleDescription += " (adjusted)"
        }
    }
    var b = SimpleStructure()
    b.adjust()
    let bDescription = b.simpleDescription

    注意,mutating关键字标记一个可以修改构造的方法。而类中的方法都可以修改类(自己),所以不必声明mutating。

    3、使用extension来扩展一个已知类的功能,可以扩展一个协议或者你引用的类。

    extension Int: ExampleProtocol {
        var simpleDescription: String {
            return "The number (self)"
        }
        mutating func adjust() {
            self += 42
        }
    }
    print(7.simpleDescription)

    4、协议和其他声明的类型一样,可以用协议的名字来创建一个集合,集合中的对象类型不同但是都遵守同一个协议。

    当你处理一个协议类型的变量时,协议外部定义的方法不可用。

    let protocolValue: ExampleProtocol = a
    print(protocolValue.simpleDescription)
    // print(protocolValue.anotherProperty)  // Uncomment to see the error

    尽管protocolValue运行时的类型为SimpleClass,编译器把它当做ExampleProtocol变量,这意味着你无法使用这个类中实现协议的代码之外的变量和属性。

    七、处理错误

    1.可以用任何实现ErrorProtocol协议的类型来表征error。

    enum PrinterError: ErrorProtocol {
        case outOfPaper
        case noToner
        case onFire
    }

    2.用throw抛出错误。用throws表示函数可能会抛出错误。

    func send(job: Int, toPrinter printerName: String) throws -> String {
        if printerName == "Never Has Toner" {
            throw PrinterError.noToner
        }
        return "Job sent"
    }

    3.用do-catch来捕捉错误并进行处理。在可能抛出错误的函数前加try关键字。可以在catch后面加上指定的error类型。

    do {
        let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
        print(printerResponse)
    } catch {
        print(error)
    }

    可以捕捉多种类型的error:

    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)
    }

    4.可以用try?将结果转换为optional类型,如果函数发生error,则执行结果为nil,否则,就是一个包含函数返回值的optional值。

    let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")
    let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner")

    5.用defer在函数返回之前写一个代码块。这个代码块无论函数是否抛出错误,都会执行。

    这样可以在setup后紧接着写cleanup代码:

    var fridgeIsOpen = false
    let 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)

    八、泛型

    1、创建一个泛型函数或类型

    func repeatItem<Item>(item: Item, numberOfTimes: Int) -> [Item] {
        var result = [Item]()
        for _ in 0..<numberOfTimes {
            result.append(item)
        }
        return result
    }
    repeatItem("knock", numberOfTimes:4)

    2、也可以构造泛型的枚举、类、结构和方法。

    // Reimplement the Swift standard library's optional type
    enum OptionalValue<Wrapped> {
        case None
        case Some(Wrapped)
    }
    var possibleInteger: OptionalValue<Int> = .None
    possibleInteger = .Some(100)

    3、在类型名字后面用where来指定一些必要元素,来获得实现一定协议的类型、类型相同或者有某个特定子类的类型。

    func anyCommonElements <T: SequenceType, U: SequenceType where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, _ rhs: U) -> Bool {
        for lhsItem in lhs {
            for rhsItem in rhs {
                if lhsItem == rhsItem {
                    return true
                }
            }
        }
        return false
    }
    anyCommonElements([1, 2, 3], [3])

    <T: Equatable>和<T where T: Equatable>是等效的。

  • 相关阅读:
    检测浏览器是否支持DOM2级规定的HTML事件
    click冒泡到body
    浏览器事件归类
    兼容主流浏览器的事件处理程序
    eventPhase三个状态测试
    鼠标滚轮事件(mousewheel)
    自定义右键菜单(contextmenu)
    数据库范式
    敏捷开发模式
    没有清晰的职业规划,跳槽会很失败
  • 原文地址:https://www.cnblogs.com/tt2015-sz/p/4797959.html
Copyright © 2020-2023  润新知