Preface · 序
本文为已归档的历史博客内容,其内容可能随着时间发生已经改变。
This article is archived from my previous blog, so the content maybe have changed now.。
Learning Swift 2.1 - Note & Code
2015.09.19 by kingcosBased on [The Swift Programming Language](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/) Swift 2 Edition
Intro: This document couldn’t be absolutely correct. Please tell me about the mistakes. Skip Welcome to Swift.
Info:
- Mac OS X 10.11
- Xcode 7+ (From HERE)
- The Swift Programming Language Swift 2 Edition (From iBooks)
- Apple Developer
- The Swift Programming Language (Swift 2) 中文版
Language Guide
Enumerations
Enumerations in Swift are first-class types in their own right. They adopt many features traditionally supported only by classes, such as computed properties to provide additional information about the enumeration’s current value, and instance methods to provide functionality related to the values the enumeration represents. Enumerations can also define initializers to provide an initial member value; can be extended to expand their functionality beyond their original implementation; and can conform to protocols to provide standard functionality.
Enumeration Syntax && Matching Enumeration Values with a Switch Statement
In the CompassPoint
example above, North
, South
, East
and West
do not implicitly equal 0
, 1
, 2
and 3
. Instead, the different enumeration members are fully-fledged values in their own right, with an explicitly-defined type of CompassPoint
.
In Swift, their names (such as CompassPoint
) should start with a capital letter.
enum bestWebsites {
case v2ex, dgtle, sspai, maimieng
}
var bw = bestWebsites.v2ex
bw = .maimieng
switch bw {
case .v2ex:
print("Way to explore.")
case .dgtle:
print("About digtle life.")
case .sspai:
print("About news of iOS & Android.")
case .maimieng:
print("My blog")
}
Associated Values
enum Apple {
case hardware(String, Int, String)
case software(String, Int)
}
var product2015 = Apple.hardware("iPhone", 5288, "16 G")
product2015 = .software("Apple Music", 10)
switch product2015 {
case .hardware(let productName, let price, let deviceRom):
print("The device name is \(productName), the price is \(price), the device rom is \(deviceRom).")
case var .software(productName, feesPerMonth):
print("The software name is \(productName), the fees per month is \(feesPerMonth)¥.")
}
// DISPLAY:
// The software name is Apple Music, the fees per month is 10¥.
Raw Values
Associated values are set when you create a new constant or variable based on one of the enumeration’s members, and can be different each time you do so.
enum platformOfProgrammingLanguage: String {
case Android = "Java"
case iOS = "Swift"
}
Implicitly Assigned Raw Values & Initializing from a Raw Value
enum platformOfProgrammingLanguage: String {
case Android
case iOS
}
print(platformOfProgrammingLanguage.iOS.rawValue)
print(platformOfProgrammingLanguage(rawValue: "C++"))
// DISPLAY:
// iOS
// nil
Recursive Enumerations
Closures
Global functions are closures that have a name and do not capture any values.
Nested functions are closures that have a name and can capture values from their enclosing function.
Closure expressions are unnamed closures written in a lightweight(adj.轻便的
) syntax that can capture values from their surrounding context.
Closure Expressions
let website = ["v2ex", "maimieng", "apple", "github"]
The Sort Method
func test1(s1: String, s2: String) -> Bool {
return s1 > s2
}
print(website.sort(test1))
// DISPLAY:
// ["v2ex", "maimieng", "github", "apple"]
Closure Expression Syntax
{ (parameters) -> returnType in statements }
var p = website.sort({ (s1: String, s2: String) -> Bool in
return s1 > s2
})
Inferring Type From Context
var p = website.sort( { s1, s2 in return s1 > s2 } )```
##### Implicit Returns from Single-Expression Closures
```swift
var p = website.sort( { s1, s2 in s1 > s2 } )
Shorthand Argument Names
var p = website.sort( { $0 > $1 } )
Operator Functions
var p = website.sort(>)
Trailing(v.跟踪
) Closures
var p = website.sort() { $0 > $1 }
var q = website.sort { $0 > $1 }
let digitNames = [
0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]
var s = numbers.map {
(var number) -> String in
var output = ""
while number > 0 {
output = digitNames[number % 10]! + output
number /= 10
}
return output
}
print(s)
// DISPLAY:
// ["OneSix", "FiveEight", "FiveOneZero"]
Capturing Values & Closures Are Reference Types
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementor() -> Int {
runningTotal += amount
return runningTotal
}
return incrementor
}
let incrementByTen = makeIncrementor(forIncrement: 10)
let incrementBySeven = makeIncrementor(forIncrement: 7)
let alsoIncrementByTen = incrementByTen
print(makeIncrementor(forIncrement: 10)())
print(makeIncrementor(forIncrement: 10)())
print(incrementByTen())
print(incrementByTen())
print(incrementBySeven())
print(incrementBySeven())
print(incrementByTen())
print(alsoIncrementByTen())
// DISPLAY:
// 10
// 10
// 10
// 20
// 7
// 14
// 30
// 40
Autoclosures
DIFFICULT TO UNDERSTAND!
An autoclosure lets you delay evaluation, because the code inside isn’t run until you call the closure.
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
let nextCustomer = { customersInLine.removeAtIndex(0) }
print(customersInLine.count)
print("Now serving \(nextCustomer())!")
print(customersInLine.count)
func serveNextCustomer(customer: () -> String) {
print("Now serving \(customer())!")
}
serveNextCustomer( { customersInLine.removeAtIndex(0) } )
print(customersInLine.count)
func serveNextCustomer(@autoclosure customer: () -> String) {
print("Now serving \(customer())!")
}
serveNextCustomer(customersInLine.removeAtIndex(0))
print(customersInLine.count)
var customerClosures: [() -> String] = []
func collectCustomerClosures(@autoclosure(escaping) customer: () -> String) {
customerClosures.append(customer)
}
collectCustomerClosures(customersInLine.removeAtIndex(0))
collectCustomerClosures(customersInLine.removeAtIndex(0))
print("Collected \(customerClosures.count) closures.")
for customerClosure in customerClosures {
print("Now serving \(customerClosure())!")
}
// DISPLAY:
// 5
// Now serving Chris!
// 4
// Now serving Alex!
// 3
// Now serving Ewa!
// 2
// Collected 2 closures.
// Now serving Barry!
// Now serving Daniella!
Functions
Defining and Calling Functions
func website(a: String) -> String {
return "http://" + a + ".com"
}
print(website("maimieng"))
// DISPLAY:
// http://maimieng.com
Function Parameters and Return Values
Functions Without Parameters
func sayHelloWorld() -> String {
return "hello, world"
}
print(sayHelloWorld())
// DISPLAY:
// hello, world
Functions With Multiple Parameters
When calling a function with more than one parameter, any argument after the first is labeled according to its corresponding parameter name.
func swap(a: Int, b: Int) -> (Int, Int) {
return (b, a)
}
print(swap(1, b: 2))
// DISPLAY:
// (2, 1)
Functions Without Return Values
func spell(s: String) {
for c in s.characters {
print(c)
}
}
print(spell("iOS"))
// DISPLAY:
// i
// O
// S
// ()
func count(s: String) -> Int {
return s.characters.count
}
func c(s: String) {
count(s)
}
print(count("Swift"))
print(c("Swift"))
// DISPLAY:
// 5
// ()
Functions with Multiple Return Values & Optional Tuple Return Types
func findMinMax(array: [Int]) -> (min: Int, max: Int)? {
if array.isEmpty { return nil }
var cMin = array[0]
var cMax = array[0]
for value in array[1..<array.count] {
if value < cMin {
cMin = value
} else if value > cMax {
cMax = value
}
}
return (cMin, cMax)
}
print(findMinMax([3, 1, 4, 1, 5, 9]))
print(findMinMax([]))
// DISPLAY:
// Optional((1, 9))
// nil
Function Parameter Names
Specifying External Parameter Names
If you provide an external parameter name for a parameter, that external name must always be used when you call the function.
func sayHello(to person: String, and anotherPerson: String) -> String {
return "Hello \(person) and \(anotherPerson)!"
}
print(sayHello(to: "Bill", and: "Ted"))
// DISPLAY:
// Hello Bill and Ted!
Omitting External Parameter Names
Because the first parameter omits its external parameter name by default, explicitly writing an underscore is extraneous(adj.无关的
).
func someFunction(firstParameterName: Int, _ secondParameterName: Int) {
}
someFunction(1, 2)
Default Parameter Values
func test(a: Int = 1) {
print(a)
}
test()
test(5)
// DISPLAY:
// 1
// 5
Variadic(adj.可变的
) Parameters
A function may have at most one variadic parameter.
func total(numbers: Double...) -> Double {
var t: Double = 0
for n in numbers {
t += n
}
return t / Double(numbers.count)
}
print(total(1, 2, 3, 4, 5))
print(total(3, 8.25, 18.75))
// DISPLAY:
// 3.0
// 10.0
Constant and Variable Parameters
func website(var s: String) -> String {
s += ".com"
return s
}
print(website("maimieng"))
// DISPLAY:
// maimieng.com
In-Out Parameters
In-out parameters cannot have default values, and variadic parameters cannot be marked as inout
. If you mark a parameter as inout
, it cannot also be marked as var
or let
.
func swap(inout a: Int, inout b: Int) {
let t = a
a = b
b = t
}
var a = 1
var b = 2
swap(&a, &b)
print("(\(a), \(b))")
// DISPLAY:
// (2, 1)
Function Types
Using Function Types
func total(a: Int, _ b: Int) -> Int {
return a + b
}
var t: (Int, Int) -> Int = total
var tl = total
print(t(1, 1))
print(tl(2, 2))
// DISPLAY:
// 2
// 4
Function Types as Parameter Types
func total(a: Int, _ b: Int) -> Int {
return a + b
}
func printT(total: (Int, Int) -> Int, _ a: Int, _ b: Int) {
print(total(a, b))
}
printT(total, 1, 1)
// DISPLAY:
// 2
Function Types as Return Types
func com(var a: String) -> String {
a += ".com"
return a
}
func http(a: String) -> String {
let b = "http://" + a
return b
}
func website(a: String) -> String {
if !a.hasPrefix("http://") {
return http(a)
}
if !a.hasSuffix(".com") {
return com(a)
}
return "CORRECT"
}
print(website("http://maimieng"))
// DISPLAY:
// http://maimieng.com
Nested Functions
Nested functions are hidden from the outside world by default, but can still be called and used by their enclosing function. An enclosing function can also return one of its nested functions to allow the nested function to be used in another scope.
func website(a: String) -> String {
func http(a: String) -> String {
let b = "http://" + a
return b
}
func com(var a: String) -> String {
a += ".com"
return a
}
if !a.hasPrefix("http://") {
return http(a)
}
if !a.hasSuffix(".com") {
return com(a)
}
return "CORRECT"
}
print(website("http://maimieng"))
// DISPLAY:
// http://maimieng.com
Control Flow
For Loops
For-In & For
// 1...9 equals to 1..<10
for i in 1...9 {
for j in 1...9 {
if (i >= j) {
print("\(j) * \(i) = \(i * j) ", terminator: "")
}
}
print("")
}
// DISPLAY:
// 1 * 1 = 1
// 1 * 2 = 2 2 * 2 = 4
// 1 * 3 = 3 2 * 3 = 6 3 * 3 = 9
// 1 * 4 = 4 2 * 4 = 8 3 * 4 = 12 4 * 4 = 16
// 1 * 5 = 5 2 * 5 = 10 3 * 5 = 15 4 * 5 = 20 5 * 5 = 25
// 1 * 6 = 6 2 * 6 = 12 3 * 6 = 18 4 * 6 = 24 5 * 6 = 30 6 * 6 = 36
// 1 * 7 = 7 2 * 7 = 14 3 * 7 = 21 4 * 7 = 28 5 * 7 = 35 6 * 7 = 42 7 * 7 = 49
// 1 * 8 = 8 2 * 8 = 16 3 * 8 = 24 4 * 8 = 32 5 * 8 = 40 6 * 8 = 48 7 * 8 = 56 8 * 8 = 64
// 1 * 9 = 9 2 * 9 = 18 3 * 9 = 27 4 * 9 = 36 5 * 9 = 45 6 * 9 = 54 7 * 9 = 63 8 * 9 = 72 9 * 9 = 81
var i = 0
for _ in 1...5 {
print(i++)
}
for var a = 0; a < 3; a++ {
print(a)
}
// DISPLAY:
// 0
// 1
// 2
// 3
// 4
// 0
// 1
// 2
While Loops
While & Repeat-While
var i = 0
while (i++ < 3) {
print(i)
}
var i = 0
repeat {
print(i)
} while (i++ < 3)
// DISPLAY:
// 1
// 2
// 3
// 0
// 1
// 2
// 3
Conditional Statements
If
Omitted.
Switch
let c: Character = "i"
switch c {
case "a", "e", "i", "o", "u":
print("c is a vowel.")
default:
print("c is not a vowel.")
}
No Implicit Fallthrough
Omitted.
Interval Matching
let a = 123
switch a {
case 1...9:
print("a is small.")
case 10...99:
print("a is medium.")
case 100...199:
print("a is big.")
default:
print("a is not sure.")
}
// DISPLAY:
// a is big.
Tuples
let cs = (1, 1)
switch cs {
case (0, 0):
print("(0, 0) is center.")
case (_, 0):
print("(\(cs.0), 0)")
case (0, _):
print("(0, \(cs.1))")
case (-2...2, -2...2):
print("(\(cs.0), \(cs.1)) is inside.")
default:
print("(\(cs.0), \(cs.1)) is not sure.")
}
// DISPLAY:
// (1, 1) is inside.
Value Bindings
let cs = (2, 1)
switch cs {
case (let x, 0):
print("\(x)")
case (0, let y):
print("\(y)")
case let (x, y):
print("(\(x), \(y))")
}
// DISPLAY:
// (2, 1)
Where
let cs = (1, -1)
switch cs {
case let (x, y) where x == y:
print("(\(x), \(y)) x == y")
case let (x, y) where x == -y:
print("(\(x), \(y)) x == -y")
case let (x, y):
print("(\(x), \(y))")
}
// DISPLAY:
// (1, -1) x == -y
Control Transfer Statements
Continue & Break
Like C, omitted.
Fallthrough
Like C.
let i = 5
var d = "The number \(i) is"
switch i {
case 2, 3, 5, 7, 11, 13, 17, 19:
d += " a prime number, and also"
fallthrough
default:
d += " an integer."
}
print(d)
// DISPLAY:
// The number 5 is a prime number, and also an integer.
Labeled Statements
label name: while condition { statements }
in switch
& while
Early Exit
func test() {
let d = ["hello": "hi"]
guard d.isEmpty else {
print("c isn't empty.")
return
}
}
test()
// DISPLAY:
// c isn't empty.
Checking API Availability
if #available(iOS 9, *) {
print("Yes")
} else {
print("No")
}
// DISPLAY:
// Yes
Collection Types
Swift provides three primary collection types, known as arrays, sets, and dictionaries, for storing collections of values.
- Arrays are ordered collections of values.
- Sets are unordered collections of unique values.
- Dictionaries are unordered collections of key-value associations.
Arrays, sets, and dictionaries in Swift are always clear about the types of values and keys that they can store. This means that you cannot insert a value of the wrong type into a collection by mistake. It also means you can be confident about the type of values you will retrieve from a collection.
Swift’s array, set, and dictionary types are implemented as generic(adj.范型
) collections.
Mutability(n.易变性
) of Collections
If you create an array, a set, or a dictionary, and assign it to a variable, the collection that is created will be mutable. This means that you can change (or mutate) the collection after it is created by adding, removing, or changing items in the collection. If you assign an array, a set, or a dictionary to a constant, that collection is immutable, and its size and contents cannot be changed.
It is good practice to create immutable collections in all cases where the collection does not need to change. Doing so enables the Swift compiler to optimize the performance of the collections you create.
Arrays
Swift’s Array
type is bridged to Foundation’s NSArray
class.
Array Type Shorthand(n.速记
) Syntax
Array<Element>
& [Element]
Creating an Empty Array
var a = [Int]()
print(a.count)
a.append(3)
print(a)
a = []
print(a,count)
// DISPLAY:
// 0
// [3]
// 0
Creating an Array with a Default Value
var a = [Double](count: 3, repeatedValue: 0)
print(a)
// DISPLAY:
// [0.0, 0.0, 0.0]
Creating an Array by Adding Two Arrays Together
var a = [Double](count: 3, repeatedValue: 0)
var b = [Double](count: 2, repeatedValue: 1.5)
var c = b + a
print(c)
// DISPLAY:
// [1.5, 1.5, 0.0, 0.0, 0.0]
Creating an Array with an Array Literal
var s: [String] = ["mai", "mieng"]
var str = ["mai", "mieng"]
if s == str {
print("s == str")
}
// DISPLAY:
// s == str
Accessing and Modifying an Array
var s: [String] = ["mai", "mieng"]
print(s.count)
print(s.isEmpty)
s.append(".com")
s += ["v2ex", ".com"]
s[3] = "sspai"
print(s[3])
s[3...4] = []
print(s)
s.insert("http://", atIndex: 0)
print(s)
print(s.removeAtIndex(0))
print(s.removeLast())
print(s)
// DISPLAY:
// 2
// false
// sspai
// ["mai", "mieng", ".com"]
// ["http://", "mai", "mieng", ".com"]
// http://
// .com
// ["mai", "mieng"]
Iterating(v.重复
) Over an Array
var s: [String] = ["mai", "mieng"]
for str in s {
print(str)
}
for (index, value) in s.enumerate() {
print("(\(index), \(value))")
}
// DISPLAY:
// mai
// mieng
// (0, mai)
// (1, mieng)
Sets
A set stores distinct values of the same type in a collection with no defined ordering. You can use a set instead of an array when the order of items is not important, or when you need to ensure that an item only appears once.
Swift’s Set
type is bridged to Foundation’s NSSet
class.
Hash Values for Set Types
Omitted.
Set Type Syntax
Set<Element>
Creating and Initializing an Empty Set
var s = Set<Character>()
print(s.count)
s.insert("!")
print(s.count)
s = []
print(s.count)
// DISPLAY:
// 0
// 1
// 0
Creating a Set with an Array Literal
var s: Set<Int> = [1, 2, 3, 4]
var set: Set = ["mai", "mieng"]
Accessing and Modifying a Set
var s: Set = [1, 2, 3, 4]
print(s.count)
if !s.isEmpty {
print("s is empty.");
}
s.insert(5)
print(s.count)
print(s.remove(3))
print(s.remove(3))
s.removeAll()
print(s.count)
if s.contains(3) {
print("s contains 3.")
} else {
print("s doesn't contain 3.")
}
// DISPLAY:
// 4
// s is empty.
// 5
// Optional(3)
// nil
// 0
// s doesn't contain 3
Iterating Over a Set
var s: Set = [1, 2, 3, 4]
for set in s {
print("\(set)")
}
for set in s.sort() {
print("\(set)")
}
// DISPLAY:
// 2
// 3
// 1
// 4
// 1
// 2
// 3
// 4
Performing Set Operations
Fundamental Set Operations
var a: Set = [1, 2, 3, 4, 5]
var b: Set = [2, 4, 6]
var c: Set = [1, 3, 5]
print(a.exclusiveOr(b).sort())
print(a.intersect(c).sort())
print(b.union(c).sort())
print(a.subtract(b).sort())
print(b.subtract(a).sort())
// DISPLAY:
// [1, 3, 5, 6]
// [1, 3, 5]
// [1, 2, 3, 4, 5, 6]
// [1, 3, 5]
// [6]
Set Membership and Equality
var a: Set = ["😂", "😊"]
var b: Set = ["😊", "😄"]
var c: Set = ["😂"]
print(a == b)
print(c.isSubsetOf(a))
print(a.isSupersetOf(a))
print(c.isDisjointWith(b))
print(c.isStrictSubsetOf(a))
print(a.isStrictSubsetOf(a))
print(a.isStrictSupersetOf(c))
print(a.isStrictSupersetOf(a))
// DISPLAY:
// false
// true
// true
// true
// true
// false
// true
// false
Dictionaries
Swift’s Dictionary
type is bridged to Foundation’s NSDictionary
class.
Dictionary Type Shorthand Syntax
Dictionary<Key, Value>
& [Key: Value]
Creating an Empty Dictionary
var d = [Int: String]()
d[404] = "not found"
print(d.count)
d = [:]
print(d.count)
// DISPLAY:
// 1
// 0
Creating a Dictionary with a Dictionary Literal
var d: [Int: String] = [404: "not found", 502: "bad gateway"]
var dy = ["height": 180, "weight": 60]
Accessing and Modifying a Dictionary
var dy = ["height": 180, "weight": 60]
print(dy.count)
dy["wage"] = 0
print(dy)
dy["height"] = 185
print(dy)
print(dy.updateValue(65, forKey: "weight"))
dy["wage"] = nil
print(dy)
print(dy.removeValueForKey("age"))
// DISPLAY:
// 2
// ["height": 180, "weight": 60, "wage": 0]
// ["height": 185, "weight": 60, "wage": 0]
// Optional(60)
// ["height": 185, "weight": 65]
// nil
Iterating Over a Dictionary
To iterate over the keys or values of a dictionary in a specific order, use the sort()
method on its keys
or values
property.
var dy = ["age": 20, "height": 180, "weight": 60, "wage": 0]
for (des, value) in dy {
print("(" + des + ", \(value))")
}
for des in dy.keys {
print(des)
}
for value in dy.values {
print(value)
}
var dyDes = [String](dy.keys)
var dyValue = [Int](dy.values)
// DISPLAY:
// (height, 180)
// (age, 20)
// (weight, 60)
// (wage, 0)
// height
// age
// weight
// wage
// 180
// 20
// 60
// 0
// ---
for d in dy.enumerate() {
print(d)
}
// ---
// DISPLAY:
// (0, ("height", 180))
// (1, ("age", 20))
// (2, ("weight", 60))
// (3, ("wage", 0))
Strings and Characters
Swift’s String
type is bridged with Foundation’s NSString
class. If you are working with the Foundation framework in Cocoa, the entire NSString
API is available to call on any String
value you create when type cast to NSString
, as described in AnyObject. You can also use a String
value with any API that requires an NSString
instance.
String Literals(n.字面量
)
let testString = "maimieng"
Initializing an Empty String
var s = ""
var str = String()
s = "hello"
if Str.isEmpty {
Str = "hi"
}
print(s + " " + str)
var Str: String
Str = "" // Initialize
if Str.isEmpty {
Str = "Hi!"
}
print(Str)
// DISPLAY:
// hello hi
// Hi!
String Mutability(n.易变性
)
var myWebsite = "maimieng"
myWebsite += ".com"
Strings Are Value Types
Behind the scenes, Swift’s compiler optimizes(v.优化
) string usage so that actual copying takes place only when absolutely necessary. This means you always get great performance when working with strings as value types.
Working with Characters
for c in "Laugh: 😂".characters {
print(c)
}
let testCharacter: Character = "."
var charArray: [Character] = ["s", "w", "i", "f", "t"]
print(charArray)
print(String(charArray))
// DISPLAY:
// L
// a
// u
// g
// h
// :
//
// 😂
// ["s", "w", "i", "f", "t"]
// swift
Concatenating(n.连接
) Strings and Characters
You can’t append a String
or Character
to an existing Character
variable, because a Character
value must contain a single character only.
var a = "mai"
var b = "mieng"
var c: Character = "."
var d = a + b
a += b
d.append(c)
print(a)
print(d)
// DISPLAY:
// maimieng
// maimieng.
String Interpolation(n.插入
)
The expressions you write inside parentheses(n.圆括号
) within an interpolated(adj.插入的
) string cannot contain an unescaped double quote ("
) or backslash (\
), and cannot contain a carriage return(回车
) or line feed(换行
).
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
print(message)
// DISPLAY:
// 3 times 2.5 is 7.5
Unicode
Swift’s String
and Character
types are fully Unicode-compliant, as described in this section.
Unicode Scalars(n.标量
)
Behind the scenes, Swift’s native String
type is built from Unicode scalar values. A Unicode scalar is a unique(adj.唯一的
) 21-bit number for a character or modifier(n.修饰符
), such as U+0061
for LATIN(adj.拉丁的) SMALL LETTER A
("a"
), or U+1F425
for FRONT-FACING BABY CHICK
("🐥"
).
NOTE A Unicode scalar is any Unicode code point in the range
U+0000
toU+D7FF
inclusive(adj.包括的
) orU+E000
toU+10FFFF
inclusive. Unicode scalars do not include the Unicode surrogate(n.替代品
) pair code points, which are the code points in the rangeU+D800
toU+DFFF
inclusive.
Note that not all 21-bit Unicode scalars are assigned to a character—some scalars are reserved for future assignment. Scalars that have been assigned to a character typically also have a name, such as LATIN SMALL LETTER A
and FRONT-FACING BABY CHICK
in the examples above.
Special Characters in String Literals
String literals can include the following special characters:
- The escaped special characters
\0
(null character),\\
(backslash),\t
(horizontal(adj.水平的
) tab),\n
(line feed),\r
(carriage return),\"
(double quote) and\'
(single quote) - An arbitrary(
adj.武断的
) Unicode scalar, written as\u{n}
, wheren
is a 1–8 digit hexadecimal number with a value equal to a valid Unicode code point
The code below shows four examples of these special characters. The wiseWords
constant contains two escaped double quote characters. The dollarSign
, blackHeart
, and sparklingHeart
constants demonstrate the Unicode scalar format:
let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
let dollarSign = "\u{24}"
let blackHeart = "\u{2665}"
let sparklingHeart = "\u{1F496}"
print(wiseWords)
print(dollarSign)
print(blackHeart)
print(sparklingHeart)
// DISPLAY:
// "Imagination is more important than knowledge" - Einstein
// $
// ♥
// 💖
Extended Grapheme(n.字形
) Clusters(n.群集
)
Every instance of Swift’s Character
type represents a single extended grapheme cluster. An extended grapheme cluster is a sequence of one or more Unicode scalars that (when combined) produce a single human-readable character.
Here’s an example. The letter é
can be represented as the single Unicode scalar é
(LATIN SMALL LETTER E WITH ACUTE(n.重读)
, or U+00E9
). However, the same letter can also be represented as a pair of scalars—a standard letter e
(LATIN SMALL LETTER E
, or U+0065
), followed by the COMBINING ACUTE ACCENT
scalar (U+0301
). The COMBINING ACUTE ACCENT
scalar is graphically applied to the scalar that precedes(v.优先
) it, turning an e
into an é
when it is rendered(v.成为
) by a Unicode-aware text-rendering system.
In both cases, the letter é
is represented as a single Swift Character value that represents an extended grapheme cluster. In the first case, the cluster contains a single scalar; in the second case, it is a cluster of two scalars:
let eAcute: Character = "\u{E9}"
let combinedEAcute: Character = "\u{65}\u{301}"
print(eAcute)
print(combinedEAcute)
// DISPLAY:
// é
// é
Extended grapheme clusters are a flexible way to represent many complex script characters as a single Character value. For example, Hangul(n.韩语
) syllables(n.音节
) from the Korean alphabet can be represented as either a precomposed(adj.预先构成的
) or decomposed(adj.已分解的
) sequence. Both of these representations qualify as a single Character value in Swift:
let precomposed: Character = "\u{D55C}"
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}"
print(precomposed)
print(decomposed)
// DISPLAY:
// 한
// 한
Extended grapheme clusters enable scalars for enclosing marks (such as COMBINING ENCLOSING CIRCLE
, or U+20DD
) to enclose other Unicode scalars as part of a single Character value:
let enclosedEAcute: Character = "\u{E9}\u{20DD}"
print(enclosedEAcute)
// DISPLAY:
// é⃝
Unicode scalars for regional indicator symbols can be combined in pairs to make a single Character
value, such as this combination of REGIONAL INDICATOR SYMBOL LETTER U
(U+1F1FA
) and REGIONAL INDICATOR SYMBOL LETTER S
(U+1F1F8
):
let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
print(regionalIndicatorForUS)
// DISPLAY:
// 🇺🇸
Counting Characters
Note that Swift’s use of extended grapheme clusters for Character
values means that string concatenation and modification may not always affect a string’s character count.
NOTE Extended grapheme clusters can be composed of one or more Unicode scalars. This means that different characters—and different representations of the same character—can require different amounts of memory to store. Because of this, characters in Swift do not each take up the same amount of memory within a string’s representation. As a result, the number of characters in a string cannot be calculated without iterating(
v.遍历
) through the string to determine its extended grapheme cluster boundaries. If you are working with particularly long string values, be aware that thecharacters
property must iterate over the Unicode scalars in the entire string in order to determine the characters for that string.
The count of the characters returned by the
characters
property is not always the same as the length property of anNSString
that contains the same characters. The length of anNSString
is based on the number of 16-bit code units within the string’s UTF-16 representation and not the number of Unicode extended grapheme clusters within the string.
let myWebsite = "我的网址是:maimieng.com 😂"
print(myWebsite.characters.count)
// DISPLAY:
// 20
var enclosedEAcute = "\u{E9}"
print(enclosedEAcute.characters.count)
enclosedEAcute += "\u{20DD}"
print(enclosedEAcute.characters.count)
// DISPLAY:
// 1
// 1
Accessing and Modifying a String
String Indices(n.索引
)
let myWebsite = "maimieng.com"
print(myWebsite[myWebsite.startIndex])
print(myWebsite[myWebsite.endIndex.predecessor()])
print(myWebsite[myWebsite.startIndex.successor()])
var index = myWebsite.startIndex.advancedBy(8)
print(myWebsite[index])
for index in myWebsite.characters.indices {
print("\(myWebsite[index])", terminator: "_")
}
// DISPLAY:
// m
// m
// a
// .
// m_a_i_m_i_e_n_g_._c_o_m_
// INCORRECT CODE:
// print(myWebsite[myWebsite.endIndex - 1])
// The 'endIndex' isn't type 'int' but 'index'.
// print(myWebsite[myWebsite.endIndex])
// print(myWebsite[myWebsite.startIndex.predecessor()])
// Runtime error: Beyond the string's range.
Inserting and Removing
var myWebsite = "maimiengcom"
myWebsite.insert(".", atIndex: myWebsite.startIndex.advancedBy(8))
print(myWebsite)
myWebsite.insertContentsOf("http://".characters, at: myWebsite.startIndex)
print(myWebsite)
myWebsite.removeAtIndex(myWebsite.endIndex.advancedBy(-4))
print(myWebsite)
let range = myWebsite.endIndex.advancedBy(-3)..<myWebsite.endIndex
myWebsite.removeRange(range)
print(myWebsite)
// DISPLAY:
// maimieng.com
// http://maimieng.com
// http://maimiengcom
// http://maimieng
Comparing Strings
String and Character Equality
String and character comparisons in Swift are not locale-sensitive(adj.本地敏感的
).(意思是 Swift 中的字符和字符串无论在哪个语言区域,其表现和功能都是相同的)
==
& !=
:
var a = "hello"
var b = "hello"
if a == b {
print("a == b")
}
// DISPLAY:
// a == b
Two String
values (or two Character values) are considered equal if their extended grapheme clusters are canonically(adv.按标准地
) equivalent. Extended grapheme clusters are canonically equivalent if they have the same linguistic(adj.语言学的
) meaning and appearance, even if they are composed from different Unicode scalars behind the scenes.
For example, LATIN SMALL LETTER E WITH ACUTE
(U+00E9
) is canonically equivalent to LATIN SMALL LETTER E
(U+0065
) followed by COMBINING ACUTE ACCENT
(U+0301
). Both of these extended grapheme clusters are valid ways to represent the character é
, and so they are considered to be canonically equivalent:
let eAcuteQuestion = "\u{E9}"
let combinedEAcuteQuestion = "\u{65}\u{301}"
if eAcuteQuestion == combinedEAcuteQuestion {
print(eAcuteQuestion + " == " + combinedEAcuteQuestion)
}
// DISPLAY:
// é == é
Conversely, LATIN CAPITAL LETTER A
(U+0041
, or "A"
), as used in English, is not equivalent to CYRILLIC CAPITAL LETTER A
(U+0410
, or "А"
), as used in Russian. The characters are visually similar, but do not have the same linguistic meaning:
let latinCapitalLetterA:Character = "\u{41}"
let cyrillicCapitalLetterA:Character = "\u{0410}"
if latinCapitalLetterA != cyrillicCapitalLetterA {
var a = "", b = ""
a.append(cyrillicCapitalLetterA)
b.append(latinCapitalLetterA)
print(a + " != " + b)
}
// DISPLAY:
// А != A
Prefix and Suffix Equality
The hasPrefix(_:)
and hasSuffix(_:)
methods perform a character-by-character canonical equivalence comparison between the extended grapheme clusters in each string.
let iPhone = [
"2007: iPhone",
"2008: iPhone 3G",
"2009: iPhone 3GS",
"2010: iPhone 4",
"2011: iPhone 4s",
"2012: iPhone 5",
"2013: iPhone 5c",
"2013: iPhone 5s",
"2014: iPhone 6",
"2014: iPhone 6 Plus",
"2015: iPhone 6s",
"2015: iPhone 6s Plus"
]
var hasC = 0
var in2015 = 0
for i in iPhone {
if i.hasSuffix("c") {
hasC++
}
if i.hasPrefix("2015") {
in2015++
}
}
print(hasC)
print(in2015)
// DISPLAY:
// 1
// 2
Unicode Representations of Strings
Omitted.
Basic Operators
Terminology(n.术语
)
Unary(adj.一元的
), binary(adj.二元的
), or ternary(adj.三元的
) operators.
int a = 10
int b = -a
a ? b : c
Assignment Operator
Assignment operator does not return a value.
let a = 10
var v = 5
v = a
let (x, y) = (1, 2)
print("(\(x), \(y))")
// DISPLAY:
// (1, 2)
// if (x = y) {
// ...
// }
// Bug:
// x = y does not return a value.
Arithmetic(n.算术
) Operators
let a = 1
let b = 5
var c: Int
c = a + b
print(c)
c = a - b
print(c)
c = a * b
print(c)
var d = Double(a) / Double(b)
print(d)
// DISPLAY:
// 6
// -4
// 5
// 0.2
Remainder(n.求余
) Operator
To determine the answer for a % b
, the %
operator calculates the following equation and returns remainder as its output:
a = (b x some multiplier) + remainder
var a = 2
var b = 5
var c = -4
print(a % b)
print(c % b)
// DISPLAY:
// 2
// -4
Floating-Point Remainder Calculations
var d = 0.3
var e = 0.8
print(e % d)
// DISPLAY:
// 0.2
Increment and Decrement Operators
var a = 1
print(a++)
print(++a)
// DISPLAY:
// 1
// 3
Unary Minus Operator & Unary Plus Operator
var a = -1
a = -a
print(a)
print(+a)
// DISPLAY:
// 1
// 1
Compound(adj.复合的
) Assignment Operators
print(1 == 5)
print(1 != 5)
print(1 >= 5)
print(1 <= 5)
print(2 > 3)
print(2 < 3)
// DISPLAY:
// false
// true
// false
// true
// false
// true
Ternary Conditional Operator
let a = true
let b = a ? 1 : 0
print(b)
let c: Int
if a {
c = 1
} else {
c = 0
}
print(c)
// DISPLAY:
// 1
// 1
Nil Coalescing(n.合并
) Operator
The nil
coalescing operator (a ?? b
) unwraps an optional a
if it contains a value, or returns a default value b
if a is nil
.
The expression a
is always of an optional type.
The expression b
must match the type that is stored inside a
.
let defaultGreeting = "Hello!"
var myGreeting: String?
print(myGreeting ?? defaultGreeting)
myGreeting = "Hi!"
print(myGreeting ?? defaultGreeting)
// DISPLAY:
// Hello!
// Hi!
Range Operators
Closed Range Operator & Half-Open Range Operator
for-in
loop:
The closed range operator (a...b
) defines a
range that runs from a
to b
, and includes the values a
and b
.
The half-open range operator (a..<b
) defines a
range that runs from a
to b
, but does not include b
.
// 1...9 equals to 1..<10
for i in 1...9 {
for j in 1...9 {
if (i >= j) {
print("\(j) * \(i) = \(i * j) ", terminator: "")
}
}
print("")
}
// DISPLAY:
// 1 * 1 = 1
// 1 * 2 = 2 2 * 2 = 4
// 1 * 3 = 3 2 * 3 = 6 3 * 3 = 9
// 1 * 4 = 4 2 * 4 = 8 3 * 4 = 12 4 * 4 = 16
// 1 * 5 = 5 2 * 5 = 10 3 * 5 = 15 4 * 5 = 20 5 * 5 = 25
// 1 * 6 = 6 2 * 6 = 12 3 * 6 = 18 4 * 6 = 24 5 * 6 = 30 6 * 6 = 36
// 1 * 7 = 7 2 * 7 = 14 3 * 7 = 21 4 * 7 = 28 5 * 7 = 35 6 * 7 = 42 7 * 7 = 49
// 1 * 8 = 8 2 * 8 = 16 3 * 8 = 24 4 * 8 = 32 5 * 8 = 40 6 * 8 = 48 7 * 8 = 56 8 * 8 = 64
// 1 * 9 = 9 2 * 9 = 18 3 * 9 = 27 4 * 9 = 36 5 * 9 = 45 6 * 9 = 54 7 * 9 = 63 8 * 9 = 72 9 * 9 = 81
Logical Operators
Logical NOT Operator
let a = true
if !a {
print("true")
} else {
print("false")
}
// DISPLAY:
// false
Logical AND Operator & Logical OR Operator
let a = true
let b = false
if a && b {
print("true")
} else if (a || b) {
print("false")
}
// DISPLAY:
// false
Combining Logical Operators
The Swift logical operators &&
and ||
are left-associative, meaning that compound expressions with multiple logical operators evaluate the leftmost subexpression first.
let a = true
let b = !a // false
let c = a && b // false
let d = a || b // true
if a && b || c && d {
print("true")
} else {
print("false")
}
// DISPLAY:
// false
Explicit(adj.明确的
) Parentheses(n.小括号
)
let a = true
let b = !a // false
let c = a && b // false
let d = a || b // true
if (a || b) && (c || d) {
print("true")
} else {
print("false")
}
// DISPLAY:
// true
The Basics
Constants and Variables
Declaring Constants and Variables
var i = 3
var Pi = 3.14, c = "V"
let myWebsite = "maimieng.com"
Type Annotations
var sumOfStu: Int
var newColor, silver, gold, space_gray: String
newColor = "Rose Gold"
Naming Constants and Variables
let π = 3.14
let 😂 = "大笑"
var 网站 = "maimieng"
网站 += ".com"
let `let` = 1
// INCORRECT CODE:
// π = 3
// R: π is a constant that can't change its value.
Printing Constants and Variables
var myMac = "MacBook Pro"
let myMacSize = 13
print(myMac + " \(myMacSize)")
// DISPLAY:
// MacBook Pro 13
Comments
// Comment of one line.
/* Comment around several lines. */
Semicolons
let Sem = ";"; print(Sem)
// DISPLAY:
// ;
Integers
Integer Bounds(n.范围
)
let minValue = Int64.min
let maxValue = UInt8.max
Int
On a 32-bit platform, Int
is the same size as Int32
.
On a 64-bit platform, Int
is the same size as Int64
.
UInt
On a 32-bit platform, UInt
is the same size as UInt32
.
On a 64-bit platform, UInt
is the same size as UInt64
.
Int
is preferred.
Floating-Point Numbers
Double
represents a 64-bit floating-point number.
Float
represents a 32-bit floating-point number.
Double
is preferred.
Double
has a precision(n.明确
) of at least 15 decimal digits.
Float
can be as little as 6 decimal digits.
Type Safety and Type Inference
let myPi = 3 + 0.14
// myPi is a double value.
Numeric(adj.数值型的
) Literals
A decimal number, with no prefix
A binary number, with a 0b
prefix
An octal(adj.八进制的
) number, with a 0o
prefix
A hexadecimal(adj.十六进制的
) number, with a 0x
prefix
var myAge = 20
var binaryAge = 0b10100
var octalAge = 0o24
var hexadecimalAge = 0x14
let velocityOfLight = 3e9
let testNum = 0xFp-2
// testNum = 3.75
var moreThanOneThousand = 001_000.102_4
Numeric Type Conversion
Integer Conversion
// INCORRECT CODE:
// var tooBig: UInt8 = UInt8.max + 1
// R: Beyond the range of UInt8.
var a: Int16 = 250
var b: Int8 = -1
var c = a + Int16(b)
// INCORRECT CODE:
// var d = a + b
// R: a & b are not the same type.
// var e = Int8(a) + b
// R: It's not safe to convert type from Int16 to Int8.
Integer and Floating-Point Conversion
var a = 0.5
var b = 2
var c = a + Double(b)
// c's value is 2.5.
var d = Int(a) + b
// d's value is 2.
var e = 2 + 0.5
// INCORRECT CODE:
// var f = a + b
// R: a & b are not the same type.
Type Aliases
typealias ElementType = Int
var a: ElementType
a = 5
Booleans
let iAmAGoodMan = true
if iAmAGoodMan {
print("You, too.")
} else {
print("You're wrong.")
}
if 2 >= 1 {
print("We are good at math.")
}
// INCORRECT CODE:
// if 1 {
// ...
// }
// R: The boolean values are only "true" & "false".
Tuples
let http404Error = (404, "Not Found")
let (statusCode, _) = http404Error
var stuInfo = (stuNum: 1415925, stuName: "V")
print(http404Error.0)
print(statusCode)
print("The status code is \(statusCode).")
print("The student number is \(stuInfo.stuNum).")
// DISPLAY:
// 404
// 404
// The status code is 404.
// The student number is 1415925.
Optionals
nil
(n.无
)
var moneyOfMine: Int? = 500
moneyOfMine = nil
If Statements and Forced Unwrapping
We can access its underlying(n.位于其下
) value by adding an exclamation(n.感叹
) mark (!
) to the end of the optional’s name.
var mySaving: Int? = 1000
if mySaving != nil {
print("I still have ¥\(mySaving!).")
} else {
print("My saving is nil.")
}
// DISPLAY:
// I still have ¥ 1000.
Optional Binding
let possibleNumber = "123"
let impossibleNumber = "maimieng"
if let actualNumber = Int(possibleNumber) {
print("\(possibleNumber) has an integer value.")
}
if let actualNumber = Int(impossibleNumber) {
print("\(impossibleNumber) has an integer value.")
} else {
print("\(impossibleNumber) could not be converted to an integer.")
}
// DISPLAY:
// 123 has an integer value.
// maimieng could not be converted to an integer.
if let firstNumber = Int("4"), secondNumber = Int("42") where firstNumber < secondNumber {
print("\(firstNumber) < \(secondNumber)")
}
// DISPLAY:
// 4 < 42
We can include multiple optional bindings in a single if
statement and use a where
clause(n.分句
) to check for a Boolean condition.
Implicitly Unwrapped Optionals
let possibleString: String? = "An optional string."
let forcedString: String = possibleString!
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString
if possibleString != nil {
print(possibleString!)
}
if assumedString != nil {
print(assumedString)
}
// DISPLAY:
// An optional string.
// An implicitly unwrapped optional string.
if let definiteString = possibleString {
print(definiteString)
}
if let definiteString = assumedString {
print(definiteString)
}
// DISPLAY:
// An optional string.
// An implicitly unwrapped optional string.
Do not use an implicitly(adv.含蓄地
) unwrapped(v.解包
) optional when there is a possibility of a variable becoming nil
at a later point. Always use a normal optional type if you need to check for a nil
value during the lifetime of a variable.
Error Handling
If no error is thrown(v.抛出
), the eatASandwich()
function is called. If an error is thrown and it matches the Error.OutOfCleanDishes
case, then the washDishes()
function will be called. If an error is thrown and it matches the Error.MissingIngredients
case, then the buyGroceries(_:)
function is called with the associated [String]
value captured(v.捕获
) by the catch
pattern.
func makeASandwich() throws {
// ...
}
do {
try makeASandwich()
eatASandwich()
} catch Error.OutOfCleanDishes {
washDishes()
} catch Error.MissingIngredients(let ingredients) {
buyGroceries(ingredients)
}
// This code is just an example.
A do
statement creates a new containing scope, which allows errors to be propagated(v.传送
) to one or more catch
clauses.
Assertions(n.断言
)
Debugging with Assertions
let myAge = -1
assert(myAge >= 0, "Age should be bigger t WRONG han 0.")
// Bug:
// assertion failed: Age should be bigger than 0.: file /Users/MaiMieng/Documents/test1/main.swift, line 12
(lldb)
When to Use Assertions
An integer subscript(adj.下标的
) index is passed to a custom subscript implementation, but the subscript index value could be too low or too high.
A value is passed to a function, but an invalid value means that the function cannot fulfill(v.实现
) its task.
An optional value is currently nil
, but a non-nil value is essential(adj.基本的
) for subsequent(adj.随后的
) code to execute(v.运行
) successfully.