zoukankan      html  css  js  c++  java
  • 2.Swift快速浏览

    传统认为,在一个新的语言的第一个程序要在屏幕上显示“Hello world!”。在Swift,可以用一行代码来完成:

    println("Hello, world!")

    如果你已经在C或Objective-C中编写过代码,这个语法看起来对你来说应该很熟悉 - 在Swift中,这行代码就是一个完整的程序。你并不需要为输入/输出或字符串处理函数导入一个单独的库。写在全局范围内的代码作为切入点程序,所以你并不需要一个main函数。你也不必在每个语句结尾写分号。

    这次浏览给你足够的信息来开始通过Swift编写代码来向您展示如何完成各种编程任务。如果你不明白的地方,在本书的其余部分都会详细的解释和介绍,请不要担心。

    注:最好的经验,打开本章,Xcode的Playgrounds。Playgrounds允许你编辑代码列表,并立即看到结果。

    简单的值

    使用let来做一个常量,var来做一个变量。常量值并不需要在编译时已知,但你必须给他确切地分配一次值。这意味着你能使用常量来命名一个值,确定一次,多个地方使用。

    var myVariable = 42
    myVariable = 50
    let myConstant = 42

    一个常量或变量必须具有相同的类型作为你要分配给它的值。但是,你并不总是有明确的写入类型。你提供一个值,当你创建一个常量或变量让编译器推断出它的类型。在上面的例子中,编译器推断myVariable的是整数,因为它的初始值是一个整数。

    如果初始值不提供足够的信息(或者如果不存在初始值),写入变量后可指定类型,由冒号分开后写它的指定类型。

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

     值永远不会隐式转换为另一种类型。如果需要的值转换为不同的类型,明确地制造一个该类型的实例。

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

    像Java中直接label+width这种方式是不能用在Swift中的

    还有一个更简单的方法,包括在字符串值:写在括号中的数值,并写在括号前添加一个反斜线()。例如:

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

    创建数组和字段使用使用方括号([]),和通过写在括号内的索引或键访问元素。

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

    要创建一个空数组或字典,使用初化的语法如下。

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

    如果类型信息可以推断,你可以写一个空数组[]和一个空字典为[:] - 例如,当您为变量设定一个新值或参数传递给一个函数。

    shoppingList = []
    occupations = [:]

    这种数组和字典只能使用静态复制方式shoppingList["ss",0],因为他们没有指定类型

    控制流

    使用if和switch来写条件语句,使用for-in,for,while,do-while做循环。周围的条件或循环变量括号是可选的。围绕本体括号是必需的。

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

    在if语句,条件必须是布尔表达式,这意味着,这样的代码,if score{...}是一个错误,它没有隐式布尔条件。

    你可以使用if和let一起工作来判断可能丢失的值。这些值被表示为可选。一个可选值包含一个值或包含无(nil)。在值的类型之后写一个问号(?),以标记这个值为可选。

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

    如果可选的值是nil,条件为false的,括号中的代码被跳过。否则,可选的值被解开并分配到常量let后面,这使得解包值可用于代码块内。

    switch支持任何类型的数据和各种比较的操作,他们并不局限于整数。

    let vegetable = "red pepper"
    switch vegetable {
    case "celery":
        let vegetableComment = "Add some raisins and make ants on a log."
    case "cucumber", "watercress":
        let vegetableComment = "That would make a good tea sandwich."
    case let x where x.hasSuffix("pepper"):
        let vegetableComment = "Is it a spicy (x)?"
    default:
        let vegetableComment = "Everything tastes good in soup."
    }

    这里不能删掉default,不然编辑器会报错

    注意let在这里只能一个模式分配一个值,也可以匹配一个模式的一部分

    在执行完上面那段代码后,只要匹配到一个情况,switch就不会继续往下执行,所以这里不需要用到其他语言中的break语法,来跳出switch。当然在循环中,你还是可以使用break语法

    你可以使用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 = 0
    for (kind, numbers) in interestingNumbers {
        for number in numbers {
            if number > largest {
                largest = number
            }
        }
    }
    println(largest)

    使用while重复一个代码块直到条件改变。一个循环的条件能在结束中替换,从而确保环运行至少一次。

    var n = 2
    while n < 100 {
        n = n * 2
    }
    println(n)
     
    var m = 2
    do {
        m = m * 2
    } while m < 100
    println(m)

    你能使用索引来保持一个循环,通过使用..<来使用一个索引范围,或者使用显示的初始化,条件,自增。下面两个循环做的是同样的事情

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

    使用..<做出一个范围,它忽略了上限值,你也可以使用...包含一个范围,包括了上限值。

    函数和闭包

    使用func来声明函数。通过名字和参数列表来调用函数。用 ->来获取函数的返回类型。

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

    使用一个数组做一个复合值,例如,从一个函数返回多个值。一个元组的元素可以按名称或编号引用。

    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])
    println(statistics.sum)
    println(statistics.2)

    函数还可以带可变数量的参数,收集他们到一个数组。

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

    函数可以嵌套。嵌套函数可以访问到外部函数中声明的变量。您可以使用嵌套函数来组织很长或很复杂代码的函数。

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

     函数实际上有一种特殊情况叫闭包:代码块可以在以后调用。在一个封闭的代码中可以访问像变量和函数这样的东西,并且是在创建封闭的范围内可用,即使在闭包在不同的范围内被执行时,您已经看见过这样的例子就是嵌套函数不同的范围。您可以通过使用括号({})并且不用写名字来封闭。在使用单独的参数和返回类型中使用。

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

    你有几种更简洁的闭包写法可选。当一个封闭的类型是已知的,诸如委托回调,可以省略它的参数类型,它的返回类型,或两者的类型。单封语句隐式返回他们唯一的语句的值。

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

    你可以按编号,而不是按名称,这种方法是指参数是在很短的闭包内特别有用。

    let sortedNumbers = sorted(numbers) { $0 > $1 }
    println(sortedNumbers)

    对象和类

    使用class后面跟一个类的名字来创建一个类。属性和函数都写在类里面,这点和其他语言差不多

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

    通过在类名后接一对小括号来创建一个类的实例。使用点语法来访问属性和实例的方法。这里和其他语言不一样,它不需要new

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

    这个版本的Shape类缺少一些重要的东西:类的初始化。可使用init创建,类似Java,C++构造函数。

    class NamedShape {
        var numberOfSides: Int = 0
        var name: String
        
        init(name: String) {
            self.name = name
        }
        
        func simpleDescription() -> String {
            return "A shape with (numberOfSides) sides."
        }
       }
    var NamedShape=NamedShape(name:"myname")

    注意self用于区分是类里面的属性还是init中的参数。如果你写了init函数,在实例化对象时,需要传入参数名加冒号,在接上参数值。这是swift的语法,和Java,C直接构造函数传值不太一样。

    你还可以使用deinit代码块来处理,对象释放时的操作。如果你要写继承,类名后接冒号,在接上父类名,不会使用例如extends这样的关键字

    如果你要覆盖子类的方法,请使用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()

    属性除了能存储简单的值,它还可以有一个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")
    println(triangle.perimeter)
    triangle.perimeter = 9.9
    println(triangle.sideLength)

    对于perimeter有一个设置器,新的值可以隐式的使用newValue,当然你也可以显示的使用自定义的字段比如

      set (arg) {
                sideLength = arg / 3.0
      }

    请注意,初始化的是,EquilateralTriangle类有三个不同的步骤:

    1.设置该子类声明属性的值。
    2.调用父类的初始化。
    3.通过改变父类中定义的属性值。你也可以使用getter,setter方法做任何额外的设置工作。

    如果你不需要计算属性,但你仍然需要提供代码在执行之前或设置之后做一些处理的话,你可以使用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")
    println(triangleAndSquare.square.sideLength)
    println(triangleAndSquare.triangle.sideLength)
    triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
    println(triangleAndSquare.triangle.sideLength)

    在类中的方法和外部函数有个重要区别。函数中的参数名称仅在函数中使用(局部参数名),但方法中的参数名字,能在你调用方法时,需要使用外部参数名。默认情况下类中方法的第一个参数名默认为局部参数,所以调用时,不需要显示输入参数名,

    但第二和后续的参数名,要显示输入,并且同时作为局部参数名称和外部参数名称,当然你也可以分别指定第二个参数或之后的参数哪个为局部名,哪个为外部名称,中间用空格区分,空格前面为外部名,空格后面为内部名称

    class Counter {
        var count: Int = 0
        func incrementBy(amount: Int, numberOfTimes times: Int) {
            count += amount * times
        }
    }
    var counter = Counter()
    counter.incrementBy(2, numberOfTimes: 7)

    当你用可选值工作时,你可以在操作符之前使用?如果在?前的值为nil,那么在?后之后的一切都被忽略,并且整个表达式的值为nil。否则可选值被解包,在?后的一切将被展开。在这两种情况下,整个表达式的值都是可选值

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

    枚举和结构体

    用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

    在上面的例子中,枚举的原始值类型为Int,所以你只需要指定的第一个原始值。原始值的其余部分会按顺序分配。您还可以使用字符串或浮点数作为原始类型枚举。使用rawValue属性来访问枚举成员的原始值。

    使用init?(rawValue:)初始化原始值的一个实例。Rank(rawValue: 3)表达式是一个可选值

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

    请注意,枚举Hearts成员上面提到的两种方法:当分配一个值到hearts常量时,枚举成员Suit.Hearts被提到了它的全名,因为这个常量没有指定明确的类型。在switch内部,枚举成员是指由缩写形式.Hearts因为self已经知道是suit。您可以使用缩写形式在任何已经的类型上。

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

    一个枚举成员的实例可以与值的实例相关联。同一枚举成员的实例可以有不同的值与它们相关联。当你创建实例时,分配一个值。分配的值和原始值是不同的:一个枚举成员的原始值对于它的所有实例是相同的,并且在定义枚举时,提供了原始值。

    例如,考虑从服务器请求关于日出和日落时间的情况。该服务器做出响应,正常的或错误的信息。

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

    协议和扩展

    使用protocol来声明一个协议

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

    类,枚举,结构体都可以采用协议,协议可以理解为java的接口。例如上面的simpleDescription{get}表示只读

    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

    注意在SimpleStructure的声明使用了关键字mutating的标记修改结构体的方法。SimpleClass是一个类,它并不需要任何方法标记为mutating,因为在一个类中的方法可以随时修改他自己。但结构体不一样,它默认是不允许内部方法修改自己属性的,

    所以加上mutating就表示,结构体也能修改他自己了

    使用extension为现有的类型添加功能,如新方法并计算属性。您可以使用扩展添加协议一致性,用来在别处声明类型,甚至,你从库或框架中导入一个类型。

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

    你能为协议命名其他的名字,例如创建一个有不同类型对象的集合都符合单一的协议,但你使用值类型的协议工作时,协议外部定义的方法不可用

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

     即使变量protocolValue具有运行时类型SimpleClass,编译器也会把他处理成ExampleProtocol类型,这意味着你不能访问实现了协议后的类,它里面的属性和方法。

    泛型

    在尖括号里面写一个名字,来生成一个泛型函数或类型

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

    你能使用函数或方法的泛型格式,用于类,枚举和结构体

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

    类型名称后使用where来指定一个列表需求,例如需求这个类型实现一个协议,需求两个类型一样,或者需求类有一个指定的子类

    func anyCommonElements <T, U where T: SequenceType, U: SequenceType, 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])

    在简单情况下,你能省略where,并且简单的在冒号后(:)写协议或类<T: Equatable>和 <T where T: Equatable>是一样的

    对于Generator不理解的朋友可以去https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_SequenceType_Protocol/index.html#//apple_ref/swift/tdef/s:PSs12SequenceType9Generator查看

  • 相关阅读:
    结巴分词 0.14 版发布,Python 中文分词库
    Lazarus 1.0.2 发布,Pascal 集成开发环境
    Android全屏 去除标题栏和状态栏
    服务器日志现 Android 4.2 传将添多项新特性
    Percona XtraBackup 2.0.3 发布
    长平狐 Android 强制设置横屏或竖屏 设置全屏
    NetBeans 7.3 Beta 发布,全新的 HTML5 支持
    CppDepend现在已经支持Linux
    GromJS 1.7.18 发布,服务器端的 JavaScript
    Apache OpenWebBeans 1.1.6 发布
  • 原文地址:https://www.cnblogs.com/tianjian/p/4597154.html
Copyright © 2011-2022 走看看