简介
- 基于成熟的Cocoa 和 Cocoa Touch 框架
- 类脚本语言,又能满足工业标准
- 支持代码预览,易入门
基本语法
简单值
使用 let 来声明常量,使用 var 来声明变量
let a = 6
let b:Int = 6
let c:Int
c = 0
值永远不会被隐式转换为其他类型。如果你需要把一个值转换成其他类型,请显式转换。
let label = "The width is"
let width = 94
let widthLabel = label + String(width)
使用方括号 [] 来创建数组和字典,并使用下标或者键(key)来访问元素。最后一个元素后面允许有个逗号。
var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"
var occupations = [
"Malcolm": "Captain",
"Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"
可以用 [] 和 [:] 来创建空数组和空字典——就像你声明变量或者给函数传参 数的时候一样。
shoppingList = []
occupations = [:]
如果你在需要使用 Bool 类型的地方使用了非布尔值,Swift 的类型安全机制会报错。
let i = 1 if i {
// 这个例子不会通过编译,会报错
}
元组
元组内的值可以是任意类型,并不要求是相同类型。
let http404Error = (404, "Not Found")
print("The status code is \(http404Error.0) message is \(http404Error.1)")
你可以将一个元组的内容分解(decompose)成单独的常量和变量,然后你就可以正常使用它们了
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
print("The status message is \(statusMessage)")
如果你只需要一部分元组值,分解的时候可以把要忽略的部分用下划线( _ )标记
let (justTheStatusCode, _) = http404Error print("The status code is \(justTheStatusCode)")
你可以在定义元组的时候给单个元素命名
let http200Status = (statusCode: 200, description: "OK")
print("The status code is \(http200Status.statusCode)")
泛型
在尖括号里写一个名字来创建一个泛型函数或者类型。
func repeatItem- (item: Item, numberOfTimes: Int) -> [Item] {
var result = [Item]()
for _ in 0..
你也可以创建泛型函数、方法、类、枚举和结构体。
enum OptionalValue {
case None
case Some(Wrapped)
}
var possibleInteger: OptionalValue = .None
possibleInteger = .Some(100)
运算符
Swift 支持大部分标准 C 语言的运算符,且改进许多特性来减少常规编码错误。赋值符( = )不返回值,以 防止把想要判断相等运算符( == )的地方写成赋值符导致的错误,Swift 还提供了 C 语言没有的表达两数之间 的值的区间运算符( a..
使用括弧来明确优先级、逻辑与(&&)或(||)非(!)比较运算(==、!=、>、 <、 >=、 <=)和其他语言用法相同 与 C 语言和 Objective-C 不同,Swift 的赋值操作并不返回任何值。所以以下代码是错误的:
if x = y {
// 此句错误, 因为 x = y 并不返回任何值
}
题 成立与否作出二选一的操作。如果 问题 成立,返回 答案1 的结果; 如果不成立,返回 答案2 的结果。let contentHeight = 40
let hasHeader = true
var rowHeight = contentHeight
if hasHeader {
rowHeight = rowHeight + 50
} else {
rowHeight = rowHeight + 20
}
//等同于
let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
• 表达式必须是Optional类型
• 默认值的类型必须要和存储值的类型保持一致let defaultColorName = "red"
var userDefinedColorName: String? //默认值为 nil
var colorNameToUse = userDefinedColorName ??defaultColorName
// userDefinedColorName 的值为空,所以 colorNameToUse 的值为 "red"
for index in 1...5 {
print("\(index) * 5 = \(index * 5)")
}
控制流
条件操作
if
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
let nickName: String? = nil
let fullName: String = "John Appleseed"
let informalGreeting = "Hi \(nickName ?? fullName)"
switch
支持任意类型的数据以及各种比较操作——不仅仅是整数以及测试相等。
let vegetable = "red pepper"
switch vegetable {
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.")
}
循环
for
可以在循环中使用 ..< 来表示范围,也可以使用传统的写法,两者是等价的。使用 ..< 创建的范围不包含上界,如果想包含的话需要使用 ... 。
var secondForLoop = 0
for var i = 0; i < 4; ++i {
secondForLoop += i
}
print(secondForLoop)
var firstForLoop = 0
for i in 0..<4 {
firstForLoop += i
}
print(firstForLoop)
for in
可以使用 for-in 来遍历字典,需要两个变量来表示每个键值对。字典是一个无序的集合,所以他们的键和值以 任意顺序迭代结束。
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
}
} }
print(largest)
while repeat
var n = 2
while n < 100 {
n=n* 2 }
print(n)
var m = 2
repeat {
m=m* 2
} while m < 100
print(m)
函数和闭包
函数
- 使用 func 来声明一个函数,使用名字和参数来调用函数。使用 -> 来指定函数返回值的类型。
func greet(name: String, day: String) -> String {
return "Hello \(name), today is \(day)."
}
greet("Bob", day: "Tuesday")
- 函数可以带有可变个数的参数,这些参数在函数内表现为数组的形式
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, condition: lessThanTen)
- 多重返回值:你可以用元组(tuple)类型让多个值作为一个复合值从函数中返回
func minMax(array: [Int]) -> (min: Int, max: Int) {
var currentMin = array[0]
var currentMax = array[0]
for value in array[1.. currentMax {
currentMax = value
} }
return (currentMin, currentMax)
}
可选元组返回,如果函数返回的元组类型有可能整个元组都“没有值”,你可以使用可选的(Optional) 元组返回类型反映整个 元组可以是 nil 的事实。
func minMax(array: [Int]) -> (min: Int, max: Int)? {
if array.isEmpty { return nil }
var currentMin = array[0]
var currentMax = array[0]
for value in array[1.. currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
- 常量参数和变量参数,函数参数默认是常量。试图在函数体中更改参数值将会导致编译错误。但是,有时候,如果函数中有传入参数的变量值副本将是很有用的。你可以通过指定一个或多个参数为变量参
数,从而避免自己在函数中定义新的变量。变量参数不是常量,你可以在函数中把它当做新的可修改副本来使
用。
func alignRight(var string: String, totalLength: Int, pad: Character) -> String {
let amountToPad = totalLength - string.characters.count
if amountToPad < 1 {
return string
}
let padString = String(pad)
for _ in 1...amountToPad {
string = padString + string
}
return string
}
let originalString = "hello"
let paddedString = alignRight(originalString, totalLength: 10, pad: "-")
print(originalString)
- 输入输出参数:变量参数,正如上面所述,仅仅能在函数体内被更改。如果你想要一个函数可以修改参数的值,并且想要在这些 修改在函数调用结束后仍然存在,那么就应该把这个参数定义为输入输出参数。定义一个输入输出参数时,在参数定义前加 inout 关键字。一个输入输出参数有传入函数的值,这个值被函数 修改,然后被传出函数,替换原来的值。
func swapTwoInts(inout a: Int, inout _ b: Int) {
let temporaryA = a
a = b
b = temporaryA
}
输入输出参数不能有默认值,而且可变参数不能用 inout 标记。如果你用 标记一个参数,这个参数不能var被或者let标记。
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
注意someInt和anotherInt在传入的时候前面都添加了&上面的 swapTwoInts 函数并没有定义任何返回值,但仍然修改了 someIn t 和 anotherInt 的值。输入输出参数是函数对函数体外产生影响的另一种方式。
- 函数类型,每个函数都有种特定的函数类型,由函数的参数类型和返回类型组成。
func printHelloWorld() {
print("hello, world")
}
这个函数的类型是: () -> void ,或者叫“没有参数,并返回 Void 类型的函数”。
在 Swift 中,使用函数类型就像使用其他类型一样。例如,你可以定义一个类型为函数的常量或变量,并将适当 的函数赋值给它:
func addTwoInts(a: Int, _ b: Int) -> Int {
return a + b
}
func multiplyTwoInts(a: Int, _ b: Int) -> Int {
return a * b
}
var mathFunction: (Int, Int) -> Int = addTwoInts
print("Result: \(mathFunction(2, 3))")
//有相同匹配类型的不同函数可以被赋值给同一个变量,就像非函数类型的变量一样
mathFunction = multiplyTwoInts
print("Result: \(mathFunction(2, 3))")
- 函数类型作为参数类型,你可以用 (Int, Int) -> Int 这样的函数类型作为另一个函数的参数类型。这样你可以将函数的一部分实现留给函 数的调用者来提供。
func printMathResult(mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
print("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)
函数实际上是一种特殊的闭包:它是一段能之后被调取的代码。闭包中的代码能访问闭包所建作用域中能得到的变 量和函数,即使闭包是在一个不同的作用域被执行的。
可以使用 {} 来创建 一个匿名闭包。使用 in 将参数和返回值类型声明与闭包函数体进行分离。
numbers.map({
(number: Int) -> Int in
let result = 3 * number
return result
})
如果一个闭包的类型已知,比如作为一个回调函数,你可以忽略参数的类型
和返回值。单个语句闭包会把它语句的值当做结果返回。
let mappedNumbers = numbers.map({ number in 3 * number })
print(mappedNumbers)
对象和类
类中属性的声明和常量、变量声明一样,唯一的区别就是它们的上下文是类。
class NamedShape {
var numberOfSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
子类如果要重写父类的方法的话,需要用 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()
枚举和结构体
枚举
使用 enum 来创建一个枚举。就像类和其他所有命名类型一样,枚举可以包含方法。
enum Rank: Int {
case Ace = 1
case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
c ase 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.rawValue
在上面的例子中,枚举原始值的类型是 Int ,所以你只需要设置第一个原始值。剩下的原始值会按照顺序赋值。
可以使用使用 rawValue 属性来访问一个枚举成员的原始值。
if let convertedRank = Rank(rawValue: 3) {
let threeDescription = convertedRank.simpleDescription()
}
stuct 结构体
使用 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()
协议和扩展
协议
协议声明了其它类可以调用的编程接口,这有点类似与java里的接口,它使得类直接的通信变的简单明了。(参考自:http://www.tuicool.com/articles/iemIbq)
协议使用 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
注意声明 SimpleStructure 时候 mutating 关键字用来标记一个会修改结构体的方法。 SimpleClass 的声明不需要 标记任何方法,因为类中的方法通常可以修改类属性(类的性质)。
struct SimpleStructure: ExampleProtocol {
var simpleDescription: String = "A simple structure"
mutating func adjust() {
simpleDescription += " (adjusted)"
}
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription
扩展
使用 extension 来为现有的类型添加功能,比如新的方法和计算属性。你可以使用扩展在别处修改定义,甚至是 从外部库或者框架引入的一个类型,使得这个类型遵循某个协议
extension Int: ExampleProtocol {
var simpleDescription: String {
return "The number \(self)"
}
mutating func adjust() {
self += 42 }
}
print(7.simpleDescription)
错误处理
你可以使用错误处理(error handling)来应对程序执行中可能会遇到的错误条件。
当一个函数遇到错误条件,它能报错。调用函数的地方能抛出错误消息并合理处理。
func canThrowAnError() throws { // 这个函数有可能抛出错误
}
一个函数可以通过在声明中添加 throws 关键词来抛出错误消息。当你的函数能抛出错误消息时, 你应该在表达式 中前置 try 关键词。
do {
try canThrowAnError() // 没有错误消息抛出
} catch {
// 有一个错误消息抛出
}
一个 do 语句创建了一个新的包含作用域,使得错误能被传播到一个或多个 catch 从句。 这里有一个错误处理如何用来应对不同错误条件的例子。
func makeASandwich() throws {
// ...
}
do {
try makeASandwich()
eatASandwich()
} catch Error.OutOfCleanDishes {
washDishes()
} catch Error.MissingIngredients(let ingredients) {
buyGroceries(ingredients)
}
断言
可选类型可以让你判断值是否存在,你可以在代码中优雅地处理值缺失的情况。然而,在某些情况下,如果值缺 失或者值并不满足特定的条件,你的代码可能没办法继续执行。这时,你可以在你的代码中触发一个断言(asser tion)来结束代码运行并通过调试来找到值缺失的原因。
使用断言进行调试
断言会在运行时判断一个逻辑条件是否为 true 。从字面意思来说,断言“断言”一个条件是否为真。你可以使用 断言来保证在运行其他代码之前,某些重要的条件已经被满足。如果条件判断为 true ,代码运行会继续进行;如 果条件判断为 false ,代码执行结束,你的应用被终止。
let age = -3
assert(age >= 0, "A person's age cannot be less than zero")
注意:
当代码使用优化编译的时候,断言将会被禁用,例如在 Xcode 中,使用默认的 target Release 配置选项来 build 时,断言会被禁用。
何时使用断言
当条件可能为假时使用断言,但是最终一定要保证条件为真,这样你的代码才能继续运行。断言的适用情景:
• 整数类型的下标索引被传入一个自定义下标脚本实现,但是下标索引值可能太小或者太大。
• 需要给函数传入一个值,但是非法的值可能导致函数不能正常执行。
• 一个可选值现在是 nil ,但是后面的代码运行需要一个非 nil 值。
参考文献
- The Swift Programming Language (Swift 2.2)
参考网址
- https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Inheritance.html
- https://swift.org
- https://github.com/apple/swift