zoukankan      html  css  js  c++  java
  • Swift翻译之-Swift语法入门 Swift语法介绍

    目录[-]

    • Hello world - Swift
    • 简单赋值
    • 控制流
    • 函数与闭包
    • 对象和类
    • 枚举与结构
    • 协议和扩展
    • 泛型

    2014.6.3日,苹果公布最新编程语言Swift,Swift是一种新的编程语言,用于iOS和OS X应用的开发,没有C的兼容性限制,Swift采用安全的编程模式,增加了现代功能,使编程变得容易,更灵活,更有趣,Swift的重新设计,依靠成熟和 备受喜爱的Cocoa, Cocoa Touch 框架,是重新构想软件如何开发的机会。

    swift 

    以下为语法介绍

    原文:A Swift Tour

    翻译:http://blog.qunee.com/2014/06/swift语法介绍/ 

    Hello world - Swift

    传统建议新语言的第一个程序是打印“Hello world”,对于Swift是下面的一行代码

    println("Hello, world")

    如果你用C或者Objective-C编写过代码,这段句法会感觉熟悉,在Swift中,这是一行完整的代码,不需要引入单独的库比如输入输出或者字符串 操作,在全局作用域下编写的代码会作为程序的入口,所以你也不需要main函数,你同样不需要在每段代码后面写分号 这次tour将教你使用Swift编程的足够信息,完成各种编程任务,如果你有不理解也不用担心,本次演示的任何内容在后面的章节中都有详细的介绍

    简单赋值

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

    有更简单的方式让字符串中包含数值:在小括号中编写数值,并在括号之前写上(),如下例:

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

    使用中括号[]创建数组和字典,通过序号或者key来访问集合中的元素

    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 = Dictionary<String, Float>()

    如果类型信息可以被断定,你可以写一个空数组[],或者一个空的字典[:],比如当你为一个变量设置新的值,或者给函数传递一个参数

    shoppingList = []   // Went shopping and bought everything.

    控制流

    使用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
      }
    }
    teamScore

    if声明中,条件必须是Boolean表达式,这意味着类似 if score{…}de的写法是错误的,不会隐式的与0比较 你可以将if和let结合使用,这意味着数值为可选,可选值可能是一个数值也可能是nil表示值缺失,在类型后面标记问号(?)表示这个值为可选

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

    如果可选值为nil,条件为flase,代码会被跳过,否则会进入并设置常量,使得变量在代码体中可用 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."
    }

    在执行完匹配的条件后,程序将跳出选择条件,不会继续到下一个条件,所以不需要在每个条件中指定break

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

    使用while重复一段代码块知道条件改变,循环条件也可以放在后面,这样可以确保循环被至少调用一次

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

    可以使用序号遍历,既可以使用.. 指定序号范围,也可以写完整的表达式

    var firstForLoop = 0
    for i in 0..3 {
      firstForLoop += i
    }
    firstForLoop
    
    var secondForLoop = 0
    for var i = 0; i < 3; ++i {
      secondForLoop += 1
    }
    secondForLoop

    函数与闭包

    使用 func声明一个函数,通过函数名和参数调用一个函数,使用 -> 声明函数返回类型

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

    使用值组表示多个数值返回

    func getGasPrices() -> (Double, Double, Double) {
      return (3.59, 3.69, 3.79)
    }
    getGasPrices()

    函数还可以设置可变参数,会将他们整合到一个数组中

    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)

    闭包实际上是函数的一种特殊形式,你可以写一个闭包,使用花括号({})包裹代码,没有函数名,使用 in 来分隔传入参数和返回类型

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

    你可以选择更简洁的闭包方式,当一个闭包类型已知,比如某个委托的回调函数,你可以省略它的参数和返回类型,单声明的闭包默认会返回这个唯一的声明变量

    numbers.map({ number in 3 * number })

    你可以通过序号替代名称来访问参数,这种方式尤其适合非常短的闭包

    sort([1, 5, 3, 12, 2]) { $0 > $1 }

    对象和类

    使用class创建类,类中的属性声明与常量和变量声明一样,只是他位于这个类的上下文中,同样的函数和方法声明也一样

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

    创建一个类实例,使用类名加括号,使用点操作符访问实例的属性和方法

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

    这个版本的Shape类缺少一些重要的东西:用于初始化这个类的构造器,可以使用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."
      }
    }

    注意,使用self来就区别传入参数的name和自身的name属性,构造器中的构造参数与函数参数的传递方式一样,所有的属性都需要指定值,可以在声明 时指定(如numberOfSides)也可以在构造函数中指定(如name) 如果你希望在对象被回收时执行清除动作,可以使用deinit创建一个析构器 子类名包含在父类名称之后,使用冒号分隔,类不需要从任何标准的根类继承,所以你可以包含或者忽略父类 子类中的重写方法需要标记上override,如果没有 override,会被编译器检查出错,同样编译器还会检测带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 triagle with sides of length (sideLength)."
      }
    }
    var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
    triangle.perimeter
    triangle.perimeter = 9.9
    triangle.sideLength

    在perimeter的setter函数中,新值隐式的命名为newValue,你也可以在set后面的括号中提供一个显式的名称 注意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")
    triangleAndSquare.square.sideLength
    triangleAndSquare.triangle.sideLength
    triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
    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.toRaw())
        }
      }
    }
    let ace = Rank.Ace
    let aceRawValue = ace.toRaw()

    上面的例子中,枚举中的原始值类型是int,所以你必须指定第一个原始值,余下的会按顺序指定,你也可以使用浮点数或者字符串作为枚举的原始类型 使用toRaw和fromRaw函数转换枚举值和原始值

    if let convertedRank = Rank.fromRaw(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)"
    }

    注意从VerverResponse中获取的日升日落的具体数值可以在switch条件中获取

    协议和扩展

    使用protocol声明一个协议

    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.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标志,是因为类中的方法总是会修改这个类 使用extension为一个已存在的类型增加函数,比如新的方法或者计算属性,你可以使用扩展,保证任意类型具有相同的协议,不管这个类型是从框架还是 类库中来的

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

    你可以像其他命名类型一样使用协议名称,比如,创建一个由类型不同但使用相同协议的对象组成的集合,当你使用这些类型时,可以当做一个协议类型来使用,协议以外的方法将不可用

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

    虽然protocolValue在运行时是SimpleClass类型,但编译器会以ExampleProtocol对待,这意味你可以避免访问协议以外的方法

    泛型

    尖括号中的名称用于创建一个泛型的函数和类型

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

    也可以用同样的方式在类,枚举,结构中使用泛型

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

    在类型名后使用where指定需求列表,比如指定类型必须实现某个协议,或者要求两种类型相同,或者要求具有某个特定的超类

    func anyCommonElements <T, U where T: Sequence, U: Sequence, T.GeneratorType.Element: Equatable, T.GeneratorType.Element == U.GeneratorType.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>是相同的效果

    参考:http://my.oschina.net/nosand/blog/272018#OSC_h3_2

  • 相关阅读:
    c:forTokens标签循环输出
    jsp转long类型为date,并且格式化
    spring中@Param和mybatis中@Param使用区别(暂时还没接触)
    734. Sentence Similarity 有字典数组的相似句子
    246. Strobogrammatic Number 上下对称的数字
    720. Longest Word in Dictionary 能连续拼接出来的最长单词
    599. Minimum Index Sum of Two Lists两个餐厅列表的索引和最小
    594. Longest Harmonious Subsequence强制差距为1的最长连续
    645. Set Mismatch挑出不匹配的元素和应该真正存在的元素
    409. Longest Palindrome 最长对称串
  • 原文地址:https://www.cnblogs.com/langtianya/p/3766483.html
Copyright © 2011-2022 走看看