第一章 简介
2014年:swift 1
2015年:swift 2
可以把Swift看成是运用现代编程理念重写的,使用安全编程(safe programming)设计,源于Objective-C, Rust, Haskell, Ruby, Python, C#, CLU等重新设计的新语言。
Swift不再需要保持和C的兼容性,使得她可以拥有很多新的属性:
自动类型推断(Type Inference) | Swift可以根据初始值自动推断出变量或常量的类型 |
范型 | 范型使得Swift可以为不同类型的类执行相同的代码 |
Collection Mutability | “Swift does not have separate objects for mutable or non-mutable containers. Instead, you define mutability by defining the container as a constant or variable.” 摘录来自: “Mastering Swift 2”。 iBooks. |
代码块(Closure Syntax) | 代码块是一份可以被传递与使用的自包含性代码 |
Optional | Optional定义了一种变量,这种变量的值不是必须的,可能没有值 |
Switch | Switch语法被显著地优化与提升了 |
多值返回 | 函数可以用tuples返回多个值 |
Operator重载 | 每个累都可以实现他们的Operator重载 |
关联性的枚举 | 可以定义比一般意义上复杂得多的枚举,不只是互相联系的值 |
Mix and Match属性:让我们可以在一个项目里面同时存在Swift和Objective-C文件。还可以让我们更新现有的Objective-C应用程序到Swift并且在Swift项目里使用原有的Objective-C库。
Playground
这里的实例代码都是在playground中运行,playground分为三个部分,左边代码,右边可以实时显示结果甚至图片,颜色等,下面是debug,debug面板是默认不现实的,可以shift + cmd + Y调出。正如其名,playground是用来尝试api和新东西的。
注释写法
/// 单行注释
/**
多行注释
*/
/**
My add function, add them together and
return results.
- parameters:
- first: The first integer
- second: second The second integer
- returns: The sum
*/
func myAdd(first: Int, second: Int) -> Int{
let results = first + second
return results
}
分号不是必须的,强烈推荐不使用分号。
括号
判断条件里面的括号不是必须的,推荐不写最外层的括号
var x = 1
var y = 2
var z = 3
if x == 1 {
print("ok")
}
花括号是必须的
判断条件里面的赋值
判断条件里的赋值是会编译出错的
if x = 1 { // 这样会报错
}
var n=0 // 没有白色空格
var m = 0 // 有白色空格
可以用逗号隔开,打印出多个值拼接在一起,也可以使用\( )来输出变量或常量值
var name = "jeff"
var language = "swift"
var message1 = " Welcome to the wonderful world of "
var message2 = "\(name) Welcome to the wonderful world of \(language)!"
print(name, message1, language)
print(message2)
print函数还可以带可选三个参数,separator表示使用什么分隔符,terminator表示使用什么结束符,toStream表示输出到哪个流
var name1 = "jeff"
var name2 = "john"
var name3 = "alice"
var line = ""
print(name1, name2, name3, separator:", ", terminator:" over", toStream: &line)
print(line)
在swift,常量是被鼓励的,在swift 2和xcode 7,如果定义了一个不变的变量会得到警告。因为定义常量可以减少错误率。常量用let定义。多个常量或者变量可以用逗号隔开。
类型安全
swift是类型安全的语言,如果我们赋值一种值到另外一种类型的变量中,会出现编译错误
var age = 25
age = "good" // 编译报错
swift会根据初始值推断变量的类型
显式定义类型
我们还可以显式地定义变量类型
var x : Float = 3.14
如果我们不初始化变量值,必须显式地定义变量类型
var para // 编译报错: error: type annotation missing in pattern
var a : Int //正确
Int
Type | Minimum | Maximum |
Int8 | -128 | 127 |
Int16 | -32768 | 32767 |
Int32 | -2147483648 | 2147283647 |
Int64 | -9,223,372,036,854,775,808 | 9223372036854775807 |
Int | -9,223,372,036,854,775,808 | 9223372036854775807 |
UInt8 | 0 | 255 |
UInt16 | 0 | 65535 |
UInt32 | 0 | 42945672945 |
UInt64 | 0 | 18446744073709551615 |
UInt | 0 | 18446744073709551615 |
进制 | 前缀 |
十进制 | 没有 |
二进制 | 0b |
八进制 | 0o |
十六进制 | 0x |
swift还支持在数字里面加下划线增加可读性
var speedOfLight = 300_000
如果没有特殊原因,推荐使用Double,不推荐使用Float,请看下面例子,同样是九位小数点的运算,Float的结果只有七位,而Double有九位。
let f : Float = 0.111_111_111 + 0.222_222_222 // 结果是0.3333333
let d : Double = 0.111_111_111 + 0.222_222_222 // 结果是0.333333333
每个类型都有一个转换方法可以调用,Int必须要转换成Double才能和Double运算。
var a : Int = 3
var b : Double = 0.14
var c = Double(a) + b
没什么特别的
字符串类型
我们可以通过for循环来访问字符串里面的每一个字符
var strHello = "hello"
for char in strHello.characters {
print(char)
}
var myName = "jeff"
var helloString = "Hello \(myName)"
一些小技巧:
var stringOne = "hElLo"
print("capitalizedString: " + stringOne.capitalizedString) // Hello
print("lowercaseString: " + stringOne.lowercaseString) // hello
print("uppercaseString: " + stringOne.uppercaseString) // HELLO
var stringOne = "Hello Swift"
var stringTwo = ""
stringOne.isEmpty //false
stringTwo.isEmpty //true
stringOne == "hello swift" //false
stringOne == "Hello Swift" //true
stringOne.hasPrefix("Hello") //true
stringOne.hasSuffix("Hello") //false”
Optional变量
可以用?来定义成Optional的变量或常量,Swift提供了三种方法来对Optional作安全检查
方法一:用!=nil判断
var stringOne: String?
if stringOne != nil {...}
方法二:if let / if var (Optional Binding)
var stringTwo: String?
if let tmp = stringTwo {...}
var stringOne: String?
var charCount = stringOne?.characters.count // return nil
枚举
通过enum和case关键字定义枚举,值得注意的是,当一个变量被推断成了枚举,后面就可以省略枚举类型直接引用枚举
enum Planets {
case Earth, Mars
}
var live = Planets.Earth
live = .Mars // 省略写法,因为前面live已经被推断为Planets类型
上面例子没有用原始类型定义枚举,下面的例子是是用原始类型定义枚举
enum Devices:String {
case iPod = "iPod"
case iPhone = "iPhone"
}
之后就可以通过Devices.iPod.rawValue来访问枚举的值,还有一个神奇的特性就是,如果定义整形的枚举,并且定义了第一个枚举的值,后面的枚举的值如果不写,则会自动自增
enum NBAStars:Int {
case Kobe = 8
case Jeff
case John
}
print("Kobe is number \(NBAStars.Kobe.rawValue)") // "Kobe is number 8\n"
print("Jeff is number \(NBAStars.Jeff.rawValue)") // "Jeff is number 9\n"
print("John is number \(NBAStars.John.rawValue)") // "John is number 10\n"
enum Product {
case Book(Double, Int, Int)
case Puzzle(Double, Int)
}
var masterSwift = Product.Book(29.9, 2015, 300)
var worldPuzzle = Product.Puzzle(9.9, 500)
switch masterSwift{
case .Book(let price, let year, let page):
print("This book is published in year \(year) with \(page) pages and is sold for \(price) dollers")
case .Puzzle:
print("This is not a book")
}
集合以及Cocoa数据类型
Array是一个有序的集合,Set是一个无序的非重复集合,Dictionary是无序的键值对集合。我们通常通过index访问Array,通过迭代访问Set,通过key访问Dictionary。Swift里面的集合只能使用同一种数据类型,不像以前的Objective-C可以使用不同类型。
Array
let arrayOne = [1,2,3]
var arrayTwo = [4,5,6]
var arrayThree = [Int]()
var myArray: [AnyObject] = [1, "Two"]
var arrayFout = [Int](count: 7, repeatedValue: 3)
var multiArrayOne = [[1,2,3], [4,5,6]]
var multiArrayTwo = [[Int]]()
一些有用的属性与方法
- first: 返回第一个元素
- last: 返回最后一个元素
- count: 返回数组长度
- isEmpty: 是否为空
- append(): 添加一个元素,使用+=也能达到同样的效果
- insert(10, atIndex: 3): 把10插入到3的位置
- 两个array可以通过+直接相连
- reverse(): 反序
- 提取从2到4的子数组:var subArray = arrayOne[2...4]
- 直接修改子数组: arrayOne[2...4] = [12, 13, 14],后面的数组长度与前面的不一定一样长
Array算法
Array还可以用排序算法来对值进行排序
var arrayFive = [9, 5, 4, 8, 2, 7]
arrayFive.sortInPlace(){$0 < $1}
居然还有过滤器
var arrayFilter = [1, 2, 3, 4, 5, 6, 7, 8, 9]
let applied = arrayFilter.filter{$0 > 3 && $0 < 7}
var city = ["Boston", "London", "Chicago", "Atlanta"]
let filtered = city.filter{$0.rangeOfString("o") != nil}
//filtered contains "Boston", "London" and "Chicago”
var arrayMap = [10, 20, 30, 40]
let appliedMap = arrayMap.map{ $0 / 10}
//applied contains 1,2,3 and 4
var arrayOneMap2 = [1, 2, 3, 4]
let appliedMap2 = arrayOne.map{ "num:\($0)"}
//applied contains "num:1", "num:2", "num:3" and "num:4
数组的遍历
第一种方法是用forEach(){}
var arrayOne = [10, 20, 30, 40]
arrayOne.forEach{ print($0) }
第二种方法是for-in
var arr = ["one", "two", "three"]
for item in arr {
print(item)
}
var arr = ["one", "two", "three"]
for (index,value) in arr.arr.enumerate() {
print"\(index) \(value)")
}
上面例子用到了Clousure属性,后文将会另作解释,如果不想在原有的数组上排序,可以使用sort()
Dictionary
字典类型是用来存储键值对的集合
var myDictionary = ["US": "American", "year": 2016]
myDictionary["year"]
var dic1 = [String:String]()
var dic2 = [String:Int]()
下面列举一些常用的属性和方法
- count
- isEmpty
- updateValue("Britain", forKey: "UK"): 将"UK"的值设为Britain,但是这个函数的返回值是修改之前的值,另外如果forKey里面的值不在字典里面,这个操作相当于增加一个键值对,并且返回nil
- countries["IN"] = nil: 删除了一个键值对
- removeValueForKey("UK"): 删除“UK”并且返回删除前的值
- removeAll(): 删除所有
SET
集合是一种无序的不重复的数据类型
- insert(): 插入一个值,如果该值已经存在,则插入操作将被忽略
- contains()
- 可以用for-in语句遍历
- remove()
- removeAll()
- union()
- unionInPlace()
- subtract(): 减去
- subtractInPlace()
- intersect(): 重叠
- intersectInPlace()
- exclusiveOr(): 在其中一个集合,但是不是重叠的
Tuple
Tuple是一种把不同类型的数据放在一个集合里面的数据结构
var team = ("Boston", "Red Sox", 97, 65, 59.9)
var team = ("Boston", "Red Sox", 97, 65, 59.99)
var (city, name, wins, losses, percent) = team
var team = ("Boston", "Red Sox", 97, 65, 59.9)
var city = team.0
var name = team.1
var wins = team.2
var loses = team.3
var percent = team.4
var team = (city:"Boston", name:"Red Sox", wins:97, loses:65, percent:59.9)
Cocoa类型
Swift还兼容Cocoa类型,有些Swift类型可以在内部转换成Cocoa类型,有些需要在外部显式地转换,这两种我们统称为bridge类型。
Swift还提供了对Foundation的封装,如果要引用Foundation数据类型,必须引用import Foundation
NSNumber
Swift编译器会自动把Int类型转换成NSNumber,但是从NSNumber转回Int需要显式地转换
var inum = 7 //Creates an Int
var dnum = 10.6 //Creates a Double
var insnum: NSNumber = inum //Bridges the Int to a NSNumber
var dnsnum: NSNumber = dnum //Bridges the Double to a NSNumber
var newint = Int(insnum) //Creates an Int from a NSNumber
var newdouble = Double(dnsnum) //Creates a Double from a NSNumber
NSString
Swift本地的String类型可以自动转换为NSString类型,但是反过来不行,只能用as关键字来转化
var nsstr: NSString = "abc"
testFunc(nsstr as String)
入门
Classes
类需要注意一下几点:
- 选择正确的名字
- 继承
- 实现protocol
- 定义private和public
class ClassName: Superclass, Someprotocol{
}
class Temperature {
var celsiusValue = 0.0
// 构造方法
init() {
}
// 类方法(静态方法)
class func randomTemperature()-> Double {
return 23.0
}
}
let aTemperatureInstance = Temperature()
Method
在swift里面,函数也叫方法,有以下几点需要注意
- 方法即可以定义在类里面也可以定义在类外面
- 方法可以有多个返回值(后面讲到)
- 实例方法用func关键字定义
- 类方法(即java里面的静态方法)用class func关键字定义
下面是完整的定义一个方法的语法
class func convertTempFromFahrenheit(fahrenheit: Double) -> Double {...}
extension
extension可以让你扩展你的类,使得一个类可以分配到多个文件,所有extension里面的东西都是共享的
extension Temperature {
...
}
Java和Swift的access control对比
Swift | Java |
public: 公开 | public: 公开 |
| protected: 类继承内可访问 |
internal: 同一个模块内可访问(默认) | package: 同一个包内可访问(默认) |
private: 同一个文件 | private: 同一个类 |
以下写法会报错:
private class SomePrivateClass {
}
func someFunction() -> SomePrivateClass {
let myClass = SomePrivateClass()
return myClass
}
private class SomePrivateClass {
}
private func someFunc() -> SomePrivateClass {
let myClass = SomePrivateClass()
return myClass
}
类属性
Swift里面有两种类属性,一种是stored properties,他们的getter 和setter是在编译器默认创建的
class Temperature {
var celsiusValue: Double
}
class Temperature {
var description: String {
get {
return "3 degree"
}
set {
}
}
}
// 我们可以省略 setter
class Temperature {
var description: String {
get {
return "3 degree"
}
}
}
// 甚至有种更简洁的写法
class Temperature {
var description: String {
return "3 degree"
}
}
private(set) var celsiusValue: Int = 0.0 // 只能在类被定义的文件中set
控制
for循环
范围操作
- n...m: 从n开始数到m,包括m
- n..<m: 不包括m
var party = ["jef", "allen", "paipaibear"]
for i in 0..<party.count {
print("party members: \(party[i])")
}
for member in party {
print("safer method to read array: \(member)")
}
class Cup {
var content: String
var temperature: String
var description: String {
return "Your cup of \(content) is \(temperature) C hot"
}
init(content: String, temperature: String) {
self.content = content
self.temperature = temperature
}
}
class TeaMaker {
func boilCupOfTea(content: String) -> Cup {
return Cup(content: content, temperature: "40 C")
}
}
var teaOrders = [
"Stephoan": "Earl Grey",
"Lukas": "Green Tea",
"Jan": "Black Tea",
"Barbara": "Early Grey",
"Margin": "Green Tea"
]
var myTeaMaker = TeaMaker()
for (guest, order) in teaOrders {
var cupOfTea = myTeaMaker.boilCupOfTea(order)
print("Hello \(guest). Beware! \(cupOfTea.description)")
}
for var index = 0; index < 10; index++ {
print(index)
}
Tuples
// 创建tuple
var http404 = (404, "Not Found")
// 解压tuple
let (statusCode, statusMessage) = http404
print("The status code is \(statusCode)")
print("The status message is \(statusMessage)")
// 还可以为tuple的值起个名字
let http200Status = (statusCode: 200, description: "OK")
print("The status code is \(http200Status.statusCode)")
print("The status message is \(http200Status.description)")
- 枚举字典里的元素
- 定义多个返回值的函数
但是不要用tuple来定义复杂的数据结构,要用class或sctruct
今天就写到这
未完待续...