首页 > 代码库 > Swift中文教程(1)-简介

Swift中文教程(1)-简介

       转载请注明 http://write.blog.csdn.net/postedit/28442151

翻译者:gumpstar




        昨天Swift刚出来,以后自己可能会在手机上开发计算机视觉应用,就打算每天学习一点点。


简介


        Swift是供iOS和OS X应用编程的新编程语言,基于C和Objective-C,而却没有C的一些兼容约束。Swift采用了安全的编程模式和添加现代的功能来是的编程更加简单、灵活和有趣。界面则基于广受人民群众爱戴的Cocoa和Cocoa Touch框架,展示了软件开发的新方向。

 

  Swift在市场存在了多年。Apple基于已有的编译器、调试器、框架作为其基础架构。通过ARC(Automatic Reference Counting,自动引用计数)来简化内存管理。我们的框架栈则一直基于Cocoa。Objective-C进化支持了块、collection literal和模块,允许现代语言的框架无混乱即可使用。感谢这些基础工作,才使得可以在Apple软件开发中引入新的编程语言。

 

  Objective-C开发者会感到Swift的似曾相识。Swift采用了Objective-C的命名参数和动态对象模型。提供了对Cocoa框架和mix-and-match的互操作性。基于这些基础,Swift引入了很多新功能和结合面向过程和面向对象的功能。

 

  Swift对新的程序员也是友好的。他是工业级品质的系统编程语言,却又像脚本语言一样的友好。他支持playground,允许程序员实验一段Swift代码功能并立即看到结果,而无需麻烦的构建和运行一个应用。

 

  Swift集成了现代编程语言思想,以及Apple工程文化的智慧。编译器是按照性能优化的,而语言是为开发优化的,无需互相折中。可以从"Hello, world"开始学起并过渡到整个系统。所有这些使得Swift成为Apple软件开发者创新的源泉。

 

  Swift是编写iOS和OSX应用的梦幻方式,并且会持续推进新功能的引入。我们对 Swift充满野心,我们迫不及待的看到你用他来做点什么。


第一个程序


我们来看看hello world

println("Hello, world")
对,这就是全部代码,只有一行!!!不需要包含库,比如<iostream>,也不需要分号,也不需要main函数,因为在全部范围(global scope)写的代码就是程序的进入点(entry point)



简单值

let语句创建一个常量,var创建一个变量,常量值不需要在编译时(compile time)就知道,但是必须只能赋值一次

var myVariable = 42
myVariable = 50
let myConstant = 42


如果要进行赋值,类型要一致.但是你不需要显示写出类型,编译器会自动推导,在上面的例子中,编译器就推导出了myVariable是一个整型.

当然可以显示指定,如下使用了一个冒号显示声明为Double类型

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

EXPERIMENT
Create a constant with an explicit type of Float and a value of 4.

let explictFloat: Float = 4

值不会隐式转化为另一种类型,必须要显示转化

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.
PHP中也有这样的格式


使用[]创建数组和字典,在方括号里用下标(index)或者键值(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.


控制流


ifswitch创建条件语句for-in,for,whiledo-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语句必须是bool类型的
这意味着if score{...}这种语句是错误的


有optional类型,跟SML有些类似.

一个optional value要么包含一个值,要么是nil,在类型后面插入?表示这是一个optional

var optionalString: String? = "Hello"
optionalString == nil
 
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
    greeting = "Hello, \(name)"
}
假如optionalName是nil,那么if语句就是false,

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语句,进入那个case后会退出



使用for-in来迭代字典key-value对

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



可以使用..来表示一个range,如下,两段代码做的事情是一样的

0..3表示[0,3),0...3表示[0,3]

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


其实感觉swift的函数就是函数式编程语言的东西


使用func关键字声明函数,使用->声明函数返回类型

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. func greet(name: String, day: String) -> String {  
  2.     return "Hello \(name), today is \(day)."  
  3. }  
  4. greet("Bob""Tuesday")  

也可以返回元组

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. func getGasPrices() -> (Double, Double, Double) {  
  2.     return (3.59, 3.69, 3.79)  
  3. }  
  4. getGasPrices()  

也可以接收变参

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. func sumOf(numbers: Int...) -> Int {  
  2.     var sum = 0  
  3.     for number in numbers {  
  4.         sum += number  
  5.     }  
  6.     return sum  
  7. }  
  8. sumOf()  
  9. sumOf(42, 597, 12)  

函数可以嵌套

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. func returnFifteen() -> Int {  
  2.     var y = 10  
  3.     func add() {  
  4.         y += 5  
  5.     }  
  6.     add()  
  7.     return y  
  8. }  
  9. returnFifteen()  

函数是第一等公民(函数式编程嘛......),函数可以返回函数,也可以接收函数

下面这个函数返回一个函数(接收一个int返回一个int的函数),

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. func makeIncrementer() -> (Int -> Int) {  
  2.     func addOne(number: Int) -> Int {  
  3.         return 1 + number  
  4.     }  
  5.     return addOne  
  6. }  
  7. var increment = makeIncrementer()  
  8. increment(7)  


下面这个函数接收一个函数(接收一个int返回bool的函数)
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. func hasAnyMatches(list: Int[], condition: Int -> Bool) -> Bool {  
  2.     for item in list {  
  3.         if condition(item) {  
  4.             return true  
  5.         }  
  6.     }  
  7.     return false  
  8. }  
  9. func lessThanTen(number: Int) -> Bool {  
  10.     return number < 10  
  11. }  
  12. var numbers = [20, 19, 7, 12]  
  13. hasAnyMatches(numbers, lessThanTen)  



闭包


函数是特殊的闭包,你可以用{}套住一个闭包而不需要指定名称,用 in 来分开参数和返回类型

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. numbers.map({  
  2.     (number: Int) -> Int in  
  3.     let result = 3 * number  
  4.     return result  
  5.     })  
这个函数使得numbers里的每一个数字都变成了原来的3倍


你可以有很多种方式书写更简介的闭包,当一个闭包的类型已知,或者返回类型已知,或者都知道,你可以省略一些类型参数。单语句的闭包隐式返回唯一语句。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. numbers.map({ number in 3 * number })  

你也可以使用数字而不是名字来访问参数,这种方法在很短的闭包中非常有用。一个传递给函数的闭包若作为最后一个参数可以立即跟在参数后
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. sort([1, 5, 3, 12, 2]) { $0 > $1 }  


note:函数式编程只学过一个学期,closure也掌握的不是很好,上面的式子是按从大到小排序么?希望懂函数式编程的大神解答。


对象与类

先创建一个简单的类

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


之后我们可以实例化,并用.操作符调用变量和方法
var shape = Shape()
shape.numberOfSides = 7
var 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."
    }
}
你也可以声明并定义deinit()函数创建析构方法


下面我们来讨论子类

使用:来继承

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

可以定义变量的get和set方法
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的set方法里,新的值有一个隐式名‘newValue’,你可以显示提供名字 set(newValue : Double){}

EquilateralTriangle的初始化方法分3阶段

  1. 定义自己声明的变量的值
  2. 调用父类构造器
  3. 修改父类变量值.任意其他的方法,如set,get也能在这里调用

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
在构造square时会设置triangle的长度,在构造triangle时会设置square的长度

类里面的方法跟普通函数有一个重要的区别。普通函数的参数只能在函数作用域有效,但是类方法的参数却在你调用时也可以使用(除了第一个参数)。参数可以有二个名字,如下。第2个参数的第2个名字在函数体里使用,但是第1个名字却可以在调用时使用。
class Counter {
    var count: Int = 0
    func incrementBy(amount: Int, numberOfTimes times: Int) {
        count += amount * times
    }
}
var counter = Counter()
counter.incrementBy(2, numberOfTimes: 7)

当你与optional打交道时,可以这样书写
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
当?前面的东西时nil时,所有?后面的东西都被忽略整个表达式就是nil.否则,?之前的值就被解读并且正常运行。
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 = http://www.mamicode.com/ace.toRaw()>

在上面的例子中,枚举的raw value时Int,所以你只需要指定第一个值Ace = 1,下面就是按顺序递增,和C++的枚举类型赋值类似。你也可以指定字符串或者浮点数为枚举类型的raw value


使用toRaw()和fromRaw()来转换raw value和枚举类型

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 的值是已知的。你可以在任何时候使用简化的方法。

使用struct创建结构体。struct支持很多类的特性(比如初始化和方法)。一个重要的不同之处在于struct拷贝传递(call by value),而class按引用传递(pass by reference)

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



一个枚举类型的实例会与一个值绑定,枚举类型相同成员的不同实例会与不同的值绑定。当创建实例的时候提供了关联的值,这个值和 raw value不同:不同实例的raw vaule是一样的.

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)"
}
success会与第一个case匹配,这就是pattern match