Hello World:
print(Hello World)
本文已使用Swift3更新。v3的主要更新见Swift 3 更新
快速上手
// 常量变量
let constVar = "100"
var value = 0
var red, green, blue: Double
// 数组
var str1 = "AAA"
var str2 = "BBB"
var array:[String] = [str1, str2]
array.append("CCC")
for str in array {
print("str: \(str)")
}
var myStr = array[2]
// 字典
var dict:[String: String] = ["Key1":"Val1", "Key2":"Val2"]
dict["Key3"] = "Val3"
dict["Key2"] = nil // delete Key2
for (key, val) in dict {
print("key: \(key), value:\(val)")
}
// 枚举
enum CollisionType: Int {
case Player = 1
case Enemy = 2
}
var type = CollisionType.Player
// 函数
func doIt() -> Int {
return 0
}
func doIt(a:Int, b:Int) -> Int {
return a+b
}
// 类
class Shape {
var numberOfSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
deinit { // 析构函数,如果你需要
}
func simpleDescription() -> String {
return "\(self.name) with \(numberOfSides) sides."
}
}
var shape = Shape(name: "Box")
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()
print(shapeDescription) // Box with 7 sides.
类型
基本类型: nil, Int, Float/Double, Bool, String, Optional
集合类型: Array, Set, Dictionary
复合类型: Tuple, Function
命名类型: Class, Struct, Enum, Protocol
Int(UInt, Int, UInt8(16,32,64), Int8(16,32,64), UInt8.min, UInt8.max), 32位平台Int为Int32, 64为Int64, UInt同理
String类型支持Unicode. Unicode标量写成u{n} ("u{24}","u{1F496}")
Double(64位浮点)、Float(32位浮点)
类型别名(type aliases)
typealias AudioSample = UInt16
var maxAmplitudeFound = AudioSample.min // maxAmplitudeFound现在是0
元组
元组类型使用逗号隔开并使用括号括起来的0个或多个类型组成的列表
var a = (1,2,”haha”)
集合类型
Swift的Array类型被桥接到Foundation中的NSArray类
var someInts = [Int]() // 空数组
var threeDoubles = [Double](repeating:0.0, count: 3) // [0.0, 0.0, 0.0]
var sixDoubles = threeDoubles + [0.1, 0.2, 0.3] // [0, 0, 0, 0.1, 0.2, 0.3]
var talkList = ["ha", "xi", "mi", "ga", "he", "wa"]
talkList[2...4] = ["hehe", "yoyo"] // 利用下标来一次改变一系列数据值
talkList // ["ha", "xi", "hehe", "yoyo", "wa"]
talkList.insert("kao", at: 0) // ["kao", "ha", "xi", "hehe", "yoyo", "wa"]
Swift的Set类型被桥接到Foundation中的NSSet类
var letters = Set<Character>()
print("letters is of type Set<Character> with \(letters.count) items.")
letters.insert("a")
letters = [] // letters 现在是一个空的 Set, 但是它依然是 Set<Character> 类型
// 使用数组字面量来构造集合
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
集合类型的哈希值,比如a==b,因此必须a.hashValue == b.hashValue,Swift 的所有基本类型(比如String,Int,Double和Bool)默认都是可哈希化的
Swift的Dictionary类型被桥接到Foundation的NSDictionary类。
let airports = ["YYZ": "Toronto Pearson", "LHR": "London Heathrow"]
let airportCodes = [String](airports.keys) // ["LHR", "YYZ"]
let airportNames = [String](airports.values) // ["London Heathrow", "Toronto Pearson"]
运算符
Swift 支持 C 语言中的全部位运算符
Swift 中是可以对浮点数进行求余的
8 % 2.5 // Swift3不在支持
8.truncatingRemainder(dividingBy: 2.5) // Swift3
Swift 提供恒等===和不恒等!==
请注意,“等价于”(用三个等号表示,===)与“等于”(用两个等号表示,==)的不同:
“等价于”表示两个类类型(class type)的常量或者变量引用同一个类实例。
“等于”表示两个实例的值“相等”或“相同”,判定时要遵照设计者定义的评判标准,因此相对于“相等”来说,这是一种更加合适的叫法。
空合运算符(a ?? b): 表达式a必须是Optional类型默认值,b的类型必须要和a存储值的类型保持一致, 是对以下代码的简短表达方法
a != nil ? a! : b
与C语言中的算术运算符不同,Swift中的算术运算符默认是不会溢出的,如果允许溢出行为,使用溢出运算符(&+,&-,&*)
Optional类型
当基础类型(整形、浮点、布尔等)没有值时,是不能使用的。一个Optional值未经初始化时为nil。
var str: String? //未被初始化,nil
var nIndex: Int? = 2 //初始化为2,Optional(2)
强制解析(forced unwrapping)
使用!来获取一个不存在的可选值会导致运行时错误。使用!来强制解析值之前,一定要确定可选包含一个非nil的值。
if convertedNumber != nil {
// 输出 "convertedNumber has an integer value of 123."
print("convertedNumber has an integer value of \(convertedNumber!).")
}
隐式解析
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 需要惊叹号来获取值
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 不需要感叹号
if assumedString != nil {
print(assumedString) // 输出 "An implicitly unwrapped optional string."
}
if let definiteString = assumedString {
print(definiteString) // 输出 "An implicitly unwrapped optional string."
}
可选链式调用
// residence为optional类型,无论numberOfRooms属性是否为optional类型,roomCount将为Int?
if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
可选链式调用提供了另一种访问numberOfRooms的方式,使用问号(?)来替代原来的叹号(!),在residence后面添加问号之后,Swift 就会在residence不为nil的情况下访问numberOfRooms.
注意,Dictionary类型的键的下标返回可选类型值,故可以用可选链式调用
var testScores = ["key1": [86, 82, 84], "key2": [79, 94, 81]]
testScores["key2"]?[0]++
流程控制
条件和循环变量的括号可以省略,语句体的大括号是必须的
if语句
条件必须是一个布尔表达式, if a {...}将报错,a不会隐形地与 0 做对比
guard的执行取决于一个表达式的布尔值。我们可以使用guard语句来要求条件必须为真时,以执行guard语句后的代码。不同于if语句,一个guard语句总是有一个else分句,如果条件不为真则执行else分句中的代码。
Switch-Case
switch中匹配到的case子句之后,程序会退出switch语句,并不会继续向下运行,所以不需要在每个子句结尾写break, 如果需要贯穿可以用 fallthrough
let vegetable = "red pepper"
switch vegetable { // Is it a spicy red pepper?
case "celery":
print("Add some raisins and make ants on a log.")
case "cucumber", "watercress":
print("That would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"):
print("Is it a spicy \(x)?")
default:
print("Everything tastes good in soup.")
}
while循环 (while,repeat-while)
var n = 2
while n < 100 {
n = n * 2
}
print(n) // 128
var m = 2
repeat {
m = m * 2
} while m < 100
print(m) // 128
for循环
你可以在循环中使用 “..<”,“...” 来表示范围,也可以使用传统的写法,两者是等价的
var nLoop1 = 0
for var i = 0; i <= 3; ++i { // Swift3中已移除这种C风格的for,且移除了++操作符
nLoop1 += i
}
var nLoop2 = 0
for i in 0...3 { // 包含3,如果用..<就不包含3
nLoop2 += i
}
函数
用func声明函数, swift中函数为一级类型
// ->后为返回值
func greet(name: String, day: String) -> String {
return "Hello \(name), today is \(day)."
}
// 使用元组可以返回多个返回值
func getGasPrices() -> (Double, Double, Double) {
return (3.59, 3.69, 3.79)
}
// 可变形参
func sumOf(numbers: Int...) -> Int {
var sum = 0
for number in numbers {
sum += number
}
return sum
}
print(sumOf(numbers: 2, 3, 1)) // 6
// 函数是一级类型,这意味着可以作为参数传递
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(list: numbers, condition: lessThanTen) // true
// 指定外部参数名
func sayHello(to person: String, and anotherPerson: String) -> String {
return "Hello \(person) and \(anotherPerson)!"
}
print(sayHello(to: "Bill", and: "Ted")) // prints "Hello Bill and Ted!"
// 如果你不想为参数设置外部参数名,用一个下划线(_)代替一个明确的参数名。
// 注意,因为第一个参数默认忽略其外部参数名称,显式地写下划线是多余的。
func someFunction(_ firstParameterName: Int, _ secondParameterName: Int) {
}
someFunction(1, 3)
注意,没有返回值的函数具有隐式的返回类型Void(或者说是空元组)
运算符函数
类和结构体可以为现有的运算符提供自定义的实现,这通常被称为运算符重载。
struct Vector2D {
var x = 0.0, y = 0.0
}
func + (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y + right.y)
}
let vector = Vector2D(x: 3.0, y: 1.0)
let anotherVector = Vector2D(x: 2.0, y: 4.0)
let combinedVector = vector + anotherVector
// combinedVector 是一个新的 Vector2D 实例,值为 (5.0, 5.0)
前缀和后缀运算符: func关键字之前指定 prefix 或者 postfix 修饰符
prefix func - (vector: Vector2D) -> Vector2D {
return Vector2D(x: -vector.x, y: -vector.y)
}
复合赋值运算符: 复合赋值运算符将赋值运算符(=)与其它运算符进行结合(如+=)。在实现的时候,需要把运算符的左参数设置成inout类型,因为这个参数的值会在运算符函数内直接被修改。
func += (inout left: Vector2D, right: Vector2D) {
left = left + right
}
注意,不能对默认的赋值运算符(=)进行重载。只有组合赋值运算符可以被重载。同样地,也无法对三目条件运算符 (a ? b : c) 进行重载。
自定义运算符: 新的运算符要使用 operator 关键字在全局作用域内进行定义,同时还要指定 prefix、infix 或者 postfix 修饰符:
prefix operator +++ {}
结合性的默认值是 none,优先级的默认值 100。参考
闭包
函数是一个特殊的闭包,swift的闭包可以理解为其他语言的类似lambda表达式,形式:
{(parameters) -> returnType in
statements
}
// closure
var numbers = [2,3,4,5]
var a = numbers.map({
(number: Int) -> Int in
let result = 3 * number
return result
})
print(a) // [6, 9, 12, 15]
// 更简单的方式: 闭包参数类型已知
var b = numbers.map({ number in 3 * number })
// 根据参数位置
let sortedNumbers = numbers.sort { $0 > $1 }
print(sortedNumbers) // [5, 4, 3, 2]
类
构造函数为init,如果需要,用self来区分属性
如果你需要在删除对象之前进行一些清理工作,使用deinit创建一个析构函数
属性里可以添加get/set,新值名默认为newValue
如果你不需要计算属性,但是仍然需要在设置一个新值之前或者之后运行代码,使用willSet和didSet
继承:class Square: NamedShape {},调用父类属性或方法使用super.XXX
子类如果要重写父类的方法的话,必须用override标记
延迟存储属性来避免复杂类中不必要的初始化: lazy
标记为final来防止它们被重写
下标脚本
struct TimesTable {
let multiplier: Int
subscript(index: Int) -> Int {
return multiplier * index
}
}
let threeTimesTable = TimesTable(multiplier: 3)
print("six times three is \(threeTimesTable[6])")
// 输出 "six times three is 18"
处理变量的可选值时,你可以在操作(比如方法、属性和子脚本)之前加?。如果?之前的值是nil,?后面的东西都会被忽略,并且整个表达式返回nil。否则,?之后的东西都会被运行。在这两种情况下,整个表达式的值也是一个可选值。
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional Square")
let sideLength = optionalSquare?.sideLength
类型转化
检查类型:用 is
向下转型:用类型转换操作符(as? 或 as!)
AnyObject 可以表示任何类类型的实例
Any 可以表示任何类型,包括函数类型
访问控制
对类、结构体、枚举可以设置访问级别。
public:可以访问同一模块源文件中的任何实体,在模块外也可以通过导入该模块来访问源文件里的所有实体。通常情况下,框架中的某个接口可以被任何人使用时,你可以将其设置为 public 级别。
internal:可以访问同一模块源文件中的任何实体,但是不能从模块外访问该模块源文件中的实体。通常情况下,某个接口只在应用程序或框架内部使用时,你可以将其设置为 internal 级别。
private:限制实体只能在所在的源文件内部使用。使用 private 级别可以隐藏某些功能的实现细节。
public 为最高(限制最少)访问级别,private 为最低(限制最多)访问级别。默认为 internal 级别。另外,子类的访问级别不得高于父类的访问级别、元组访问级别为成员里最低级别。
枚举和结构体
使用enum来创建一个枚举。就像类和其他所有命名类型一样,枚举可以包含方法
enum Suit {
case Spades, Hearts, Diamonds, Clubs
func description() -> 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.description()
枚举成员可以有实例值,相同枚举成员的实例可以有不同的值。创建实例的时候传入值即可。实例值和原始值是不同的:枚举成员的原始值对于所有实例都是相同的,而且你是在定义枚举的时候设置原始值。
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)."
print(serverResponse)
case let .Error(error):
let serverResponse = "Failure... \(error)"
print(serverResponse)
}
结构体和类基本类似,但最大一个区别在于结构体是传值,类是传引用。
struct Card {
var suit: Suit
func description() -> String {
return "The Card is suit of \(suit.description)"
}
}
协议和拓展
protocol
使用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."
}
}
实现协议中的 mutating 方法时,若是类类型,则不用写mutating关键字。而对于结构体和枚举,则必须写 mutating 关键字。
类只能单继承,协议的实现可以同时实现多个。并且协议间可以相互继承,并也能多继承。在协议继承列表中,添加class声明只能被类采纳。
class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {/*...*/}
protocol InheritingProtocol: SomeProtocol, AnotherProtocol {/*...*/}
协议只指定属性的名称和类型,还指定属性是只读的还是可读可写的。指定方法时不需要大括号和方法体。
协议可以指定构造器,只是在实现类中要添加required
协议前添加@objc,表示只能被objective-c的类或@objc类采纳。
extension
使用extension来为现有的类型添加功能,比如新的方法和计算属性。你可以使用扩展在别处修改定义,甚至是从外部库或者框架引入的一个类型,使得这个类型遵循某个协议。
extension Int: ExampleProtocol {
var simpleDescription: String {
return "The number \(self)"
}
mutating func adjust() {
self += 42
}
}
print(7.simpleDescription)
泛型
在尖括号里写一个名字来创建一个泛型函数或者类型。你也可以创建泛型函数、方法、类、枚举和结构体。
func repeatItem<Item>(item: Item, numberOfTimes: Int) -> [Item] {
var result = [Item]()
for _ in 0..<numberOfTimes {
result.append(item)
}
return result
}
repeatItem(item: "knock", numberOfTimes:4)
// Reimplement the Swift standard library's optional type
enum OptionalValue<Wrapped> {
case None
case Some(Wrapped)
}
var possibleInteger: OptionalValue<Int> = .None
possibleInteger = .Some(100)
关联类型
定义一个协议时,通过 typealias 关键字来指定关联类型。
protocol A {
typealias ItemType
/*采用ItemType的一些方法*/
}
class SubA : A {
typealias ItemType = Int
}
错误处理
func canThrowAnError() throws {
// 这个函数有可能抛出错误
}
// 抛出错误消息,catch捕捉
do {
try canThrowAnError()
// 没有错误消息抛出
} catch {
// 有一个错误消息抛出
}
将错误转换成可选值
func someThrowingFunction() throws -> Int {/*...*/}
// someThrowingFunction()抛出一个错误,x和y的值是nil
let x = try? someThrowingFunction()
let y: Int?
do {
y = try someThrowingFunction()
} catch {
y = nil
}
禁用错误传递
let photo = try! loadImage("./Resources/John Appleseed.jpg")
// 禁用错误传递
指定清理操作
使用defer语句在即将离开当前代码块时执行一系列语句
func processFile(filename: String) throws {
if exists(filename) {
let file = open(filename)
defer {
close(file)
}
while let line = try file.readline() {
// 处理文件。
}
// close(file) 会在这里被调用,即作用域的最后。
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。