Welcome to Swift

About Swift

Swift是针对iOS和OS X应用开发的编程语言,它吸取了C和OC的精华,撇弃了兼容C的限制。Swift采用了安全编程模式并且增加了现代特色,让编程变得更加容易,更加灵活并且更加逗逼有趣。Swift背后是有强大的Cocoa和Cocoa Touch框架支撑着,这让我们对未来的软件开发浮想联翩。
Swift已经被开发了好几年。苹果通过提高编译器,调试器和框架设施为Swift打下了坚实的基础。因着ARC简化了存储管理。牢牢的基于Foundation和Cocoa的框架栈被现代化和标准化。OC自己也演化为支持blocks,collection literals,和modules,使得现代语言技术对框架的引用不带来破坏。感谢这些搭好的基础,我们现在可以为苹果软件开发的将来引进一个新的语言。
Swift对于OC开发者来说非常熟悉。它采取了OC命名参数的可读性和OC动态对象模型的强大性。它提供了和现存Cocoa框架的无缝连接并且能够与OC代码混合使用。Swift引进了许多新的特色,统一了过程和面向对象的部分。
Swift对于新的编程者表现的非常友好。这是第一个表现的和脚本语言一样,富有表达力并且令人感到愉悦的工业级系统编程语言。它所支持的操场特色是一个革新的特色,允许编程人员对Swift代码进行实验并且马上能看到结果,这不带来build和运行程序的开销。
Swift结合了现代语言最优良的部分。编译器优化了性能,语言优化了开发。所有这些使得Swift对于开发者和苹果来说都是一笔合理的未来投资。
使用Swift创造iOS和OS X应用非常棒,它会继续衍生出新的特色和性能。我们对Swift抱有极大的野心。我们等不及看看你们能使用Swift到什么程度。

A Swift Tour

在学习新语言的时候,我们的传统是打印出你好,世界。在Swift中,这可以由一行轻松完成:

println​(​"Hello, world!"​)

如果你使用C或者OC写过代码,这个语法应该对你很熟悉-在Swift中,这一行代码完全就是一个小程序。你不需要为输入输出或字符串处理导入库。写在全局视野的代码被作为程序的入口,所以你不需要main函数。你也不需要在每一行的结尾加上分号。
这个短途旅程通过向你展示如何完成不同的编程任务来提供给你足够的信息,让你能够开始使用Swift编写代码。如果你不理解一些事,不要担忧-在这个短途旅程中遇到的任何让你困扰的美景都会在本书的后面进行详细的剖析。

注意 为了给你最好的体验,在Xcode中开辟一个操场。操场允许你编辑代码并且立即得到结果反馈。

Simple Values

使用let创造一个常量,使用var创造一个变量。常量的值不需要在编译时就需要知道,但是你必须只分配给它值一次。这意味着你能使用常量命名一个值,但只能一次,而使用可以多次

var​ ​myVariable​ = ​42
​myVariable​ = ​50let​ ​myConstant​ = ​42

常量或者变量必须和值的类型一样。然而,你不需要总是将类型写出来。当你生成一个常量或者变量时只需要提供一个值,让编译器推断它的类型。在上面的例子中,编译器推断myVariable是一个整数,因为初试值是一个整数。
如果初试值没有提供足够的信息(或者如果没有初始值),在变量的后面加上类型,用冒号分隔。

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

值永远不会暗暗地转换成另一个类型。如果你需要将一个值转换为另一种类型,你需要明确的提出来。

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

还有一种更简单的方式将值嵌入到字符串中
将值放在括号里,并且在括号前面加一个\。举个例子

let apples=3
let oranges=5
let applesSummary="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=[String:Float]()

如果你的类型信息能够被推断出来,你可以将空数组写为[],空字典写为[:]-举个例子,当你为变量分配一个新的值或者给函数传递一个参数。

shoppingList=[]
occupations=[:]

#

Control Flow
使用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

注意 在上面的代码中,teamScore写在单独的一行,在操场中这是一个查看变量值的简单方法。

在if语句中,条件必须是布尔表达式-这意味着如果代码是if score{…}是一个错误。
你可以让if和let配搭和可能缺失的值打交道。这些值以optionals表示。一个optional值要么包含一个值要么包含一个nil表明这个值缺失。在值类型后面加上一个❓表明这个值是optional。

var optionalString:String?="Hello"
optionalString=nil

var optionalName:String?="John Appleseed"

var greeting="Hello"
if let name=optionalName{
    greeting="Hello, \(name)"
}

如果optional值是nil,这个条件是false,括号内的代码跳过不执行。否则,这个optional值解包裹,并且赋给let后面的常量,这样解包裹后的值可以在代码块中被使用。
Switches支持任意类型的数据和一大堆比较操作符-它们不局限于整数且只比较等于。

let vegetable="red pepper"
switch vegetable{
    case "celery":
    let vegetaleComment="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."
}

注意let可以被使用在一个模式上,将匹配一个模式的那部分值赋给一个常量。
在执行switch case匹配的代码后,这个程序从switch语句中撤出。并不继续执行下去,所以在每一句case代码以后并不需要break语句。
你使用for-in提供一对key-value来迭代一个字典里的items。字典是一个没有顺序的集合。所以它们的keys和values以任意顺序迭代。

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*=2
}
n

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

你可以在循环中持有一个索引-要么通过使用..<来制作索引范围或者写一个显示的初始化,条件和增量。这两个循环做的是一样的事。

var firstForLoop=0
for i in 0..<4{
    firstForLoop+=i
}
firstForLoop

var secondForLoop=0
for var i=0;i<4;++i{
    secondForLoop+=i
}
secondForLoop

使用..<来制作一个省略上限值的范围,使用…来制作包括两个值的范围。

Functions and Closures

使用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<min{
            min=score
        }else if score>max{
            max=score
        }
        sum+=score
    }
    return(min,max,sum)
}
let statistics=calculateStatistics([5,3,100,3,9])
statistics.sum
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)

函数实际上是closures的一种特殊例子,closures就是能够稍后调用的代码块。closures中的代码可以访问closures被定义的视野内的变量和函数,即使这个closure是在一个不同的地方被执行-在嵌套函数中你已经看到了这种例子。你可以写一个closure而不需要名字,将代码用({})包裹。使用in来分隔参数和体中返回的类型。

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

你可以有更精炼的方法写closures。当一个closure的类型已知时,例如一个代理的回调,你可以省略它的参数的类型,它返回的类型或者两者都可以省略。单个语句closure返回它们的语句的值。

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

你可以通过数字而不是名字引用参数-这个方法在简洁的closure中非常有用。当一个closure作为函数的最后一个参数时,它可以立刻出现在括号后面。

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

Objects and Classes

使用class后面紧跟class名字来创造一个class。在类中声明特征和声明常量或者变量一样,除了声明环境不同而已,方法或者函数声明也是同样的写法。

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

在类名字后面加上括号可以生成一个类实例。使用dot语法可以访问实例的特征和方法。

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 lenth \(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 * 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")
triangle.perimeter
triangle.perimeter=9.9
triangle.sideLength

在perimeter的set中,新值有一个暗号名字,叫做newValue。
注意这个类的初始器有三个不同的步骤

  1. 给子类声明的特征设定值
  2. 调用父类的初始器
  3. 改变父类定义的特征的值
    如果你不需要计算特征的值,但是仍然需要在设定新值前后提供代码,使用willSet和didSet。例如,下面的类确保了triangle的sideLength始终等于它的squre的sideLength。
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)

当和optional值打交道时,你可以在操作前加上❓,这些操作可以是方法,特征和下标。如果在❓之前的值是nil。所有在❓后面的东西都忽略了,整个表达式的值就是nil,否则,这个值被解包裹,❓后面的东西都作用于这个值。在两种情形下,整个表达式的值是optional值。

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

Enumerations and Structures

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

在上面的例子中,枚举的原始值类型是Int,所以你只需要指定第一个原始值。剩余的原始值按照顺序赋值。你也可以使用字符串或者浮点数作为枚举的原始类型。使用rawType特征来访问一个枚举成员的原始值。
使用init?(rawValue)初始器从一个原始值生成一个枚举实例。

let ace=Rank.Ace
let aceRawValue=ace.simpleDescription()

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和类具有许多相同的行为,包括方法和初始器。struct和类之间一个最大的差别就是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 thressOfSpadesDescription=threeOfSpades.simpleDescription()

一个枚举成员的实例可以具有关联的值。相同枚举成员的实例可以有不同的关联值。当你生成这个实例时,你可以提供这个关联值。关联值和原始值不一样。一个枚举成员的原始值对所有的实例都是一样的,当你定义这个枚举的时候,你提供这个原始值。
例如,从一个服务器请求太阳升起和太阳落下的时间。这个服务器要么响应信息要么返回错误信息。

enum ServerResponse{
    case Result(String,String)
    case Error(String)
}

let success=ServerResponse.Result("6:00 am", "8.09 pm")
let failer=ServerResponse.Error("Out of cheese")

switch success{
case let .Result(sunrise,sunset):
    let servertResponse="Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
    let serverResponse="Failure... \(error)"
}

注意日升日落时间是怎么样从ServerResponse中抽取出来的。

Protocols and Extensions

使用protocol声明一个protocol

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

类,枚举和结构都可以支持协议。

var a=SimpleClass()
a.adjust()
let aDescription=a.simpleDescription

struct SimpleStruct:ExampleProtocol {
    var simpleDescription:String="A simple structure"
    mutating func adjust() {
        simpleDescription+=" (adjusted)"
    }
}
var b=SimpleStruct()
b.adjust()
let bDescription=b.simpleDescription

注意SimpleStruct中使用mutating关键字来标记一个修改struct的方法。SimpleClass的声明不需要将方法标记为mutating因为class中的方法总是可以修改这个class。
使用extension来对存在的类型添加功能,例如新的方法和特征。你可以使用一个extension给定义在别处的类型增加支持的协议,这个类型甚至可以是从一个库或者框架得到的。

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

7.simpleDescription

你可以像其它命名的类型一样使用protocol名字-例如,生成一个对象集合,这些对象有不同的类型但是都遵循某个协议。当你和一个类型是protocol类型的值打交道时,定义在protocol外的方法是访问不了的。

let  protocolValue:ExampleProtocol=a
protocolValue.simpleDescription

虽然这个protocolValue变量在运行时的类型是SimpleClass,编译器将它视作ExampleProtocol类型。这意味着你不能访问类中不是方法定义的方法和特征。

Generics

将名字写在尖括号中来制作一个通用函数或者类型

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

你可以制作函数和方法,类,枚举和结构的通用形式。

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,在冒号后面加上协议或者类名字。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值