0.前言

总是开新坑,总是不填坑。不过以前是兴趣,现在是工作需求,应该能有一点记录的。因为工作,开始了Mac APP开发,Objective-C还没上手,又要开始用Swift去做新项目。

不过自然不会去翻译书,只是记录一些遇到的有点新东西,顺便也方便以后的sharing。

1. 变量

1.1 var和let

在swift中,定义常数使用关键字let,定义变量使用关键字let .

在使用letvar时,需要声明变量的类型,如果没有明确说明,编译器会根据实际值自行确定类型.e.g.

1
2
3
4
5
6
let implicitInteger = 70
let implicitDouble = 70.0
let explicitDouble: Double = 70

var myVariable = 42
myVariable = 50//can be changed

swift is a type safe language.

2. 字符串 数组 集合 字典

可以使用String()进行强制类型转换,使用\()把数据加入到字符串中.使用"""定义多行数组

1
2
3
4
5
6
7
8
9
let apples = 3
let oranges = 5
let appleSummary = "I have \(apples) apples."
let fruitSummary = "I have \(apples + oranges) pieces of fruit."

let quotation = """
I said "I have \(apples) apples."
And then I said "I have \(apples + oranges) pieces of fruit."
"""

swift中var声明的字符串是可变的,不同于OC中需要区分NSString与NSMutableString
字符串可使用+,count,append

可以使用索引来访问和修改字符串,但不是传统数组的方式.

使用 startIndex 属性可以获取一个 String 的第一个 Character 的索引。使用 endIndex 属性可以获取最后一个 Character 的后一个位置的索引。因此,endIndex 属性不能作为一个字符串的有效下标。如果 String 是空串,startIndexendIndex是相等的。

通过调用 String 的 index(before:)index(after:) 方法,可以立即得到前面或后面的一个索引。你还可以通过调用 index(_:offsetBy:)方法来获取对应偏移量的索引,这种方式可以避免多次调用 index(before:)index(after:)方法。

e.g.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let greeting = "Guten Tag!"
greeting[greeting.startIndex]
// G
greeting[greeting.index(before: greeting.endIndex)]
// !
greeting[greeting.index(after: greeting.startIndex)]
// u
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index]
// a

for index in greeting.indices {
print("\(greeting[index]) ", terminator: "")
}

子字符串重用原String的内存空间

  • 数组与字典

    可以使用[]来建立并访问数组和字典.e.g.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var shoppingList = ["catfish", "water", "tulips"]
    shoppingList[1] = "bottle of water"
    print(shoppingList)
    var occupations = [
    "Malcolm": "Captain",
    "Kaylee": "Mechanic",
    ]
    occupations["Jayne"] = "Public Relations"

    var emptyArray = [String]()
    var emptyDictionary = [String: Float]()
    var letters = Set<Character>()

    数组通过下标访问修改变量,下标可以是一个范围.可以使用+=在末尾添加成员,可以使用append(_:)在数组后面添加新的数据项.

    可以调用insert(_:at:)remove(_:at_)在某个索引位置添加删除成员(字符串也可以)

    如果我们同时需要每个数据项的值和索引值,可以使用 enumerated() 方法来进行数组遍历。e.g.

    1
    2
    3
    4
    5
    6
    7
    8
    for (index, value) in shoppingList.enumerated() {
    print("Item \(String(index + 1)): \(value)")
    }
    // Item 1: Six eggs
    // Item 2: Milk
    // Item 3: Flour
    // Item 4: Baking Powder
    // Item 5: Bananas

    集合可以做交集 并集 差集 是否为父子集等操作.

    更新字典时使用方法updateValue(_:forKey:)时,将返回更新之前的值,这样可以检查更新是否成功.返回值是可选值类型.

    对某个键赋值nil可以移除该键,也可以使用removeValue(forKey:)来移除.

3.Control Flow

3.1 条件语句

条件语句使用ifswitch

3.2 可选值(Optional Value)

在if语句中,可以将ifvar结合起来处理可能缺省的变量.这样的变量就是可选值.可选值即可以是具体的值,也可以是nil,在变量类型后添加?来标记可选值.

e.g.

1
2
3
4
5
6
7
8
var optionalString: String? = "Hello"
print(optionalString == nil)

var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}

OC中,nil是个指向不存在对象的指针,所以只有对象才可以是nil,Swift中nil则是一个确定的值,当值缺省时就是nil.即使是基础类型,没值也是nil.为了使没有初始值的值可以被使用,便产生了Optional Value.

Optional Value本质上是一个枚举类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
enum Optional<T> : Reflectable, NilLiteralConvertible {
case None
case Some(T)
init()
init(_ some: T)

/// Haskell's fmap, which was mis-named
func map<U>(f: (T) -> U) -> U?
func getMirror() -> MirrorType
static func convertFromNilLiteral() -> T?

var number:Int? = 32
var number:Optioanl<Int>=32//相互等价
}

可以通过解包(unwrap)来获取Optional Value的实际状态.

  • 显性解包

    1)optional binds

    if letif var搭配在一起使用时,可以判断有值或是没值,可用于条件表达式.如2.2开头的例子中的if let name = optionalName.

    2)通过!

    1
    2
    3
    4
    var str:String? = "Hello World"
    print(str) //{Some "Hello World"}

    print(str!) //Hello World
  • 隐形解包
    通过在声明时的数据类型后面加一个!来实现:

    1
    2
    var str:String! = "Hello World"
    let implict :String = str

    可以通过Optional Chaining来安全地访问Optional的属性或者方法

    当一个Optional值调用它的另一个Optional值的时候,Optional Chaining就形成了,基本上,Optional Chaining就是总是返回一个Optional的值,只要这个Chaining中有一个值为nil,整条Chaining就为nil.

    Optional Chaining除了能将属性返回的类型变为Optional外,连方法的返回值都能强制变为Optional.

    e.g.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //不使用Optional Chaining需要判断两次
    if let pet = jackon.pet {
    if let toy = pet.favoriteToy {
    toy.name
    }
    }
    //使用Optional Chaining只需要判断一次
    if let toy = jackon.pet?.favoriteToy {
    toy.name
    }

当Optional解包后的值为nil时,可以通过??来设置一个默认值

1
2
3
let nickName: String? = nil
let fullName: String = "John Appleseed"
let informalGreeting = "Hi \(nickName ?? fullName)"

3.3 switch-case

1
2
3
4
5
6
7
8
9
10
11
12
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.")
}

执行完case之后,程序会退出,所以不需要break

case分支也可以是一个值的区间,可以是元组,元组可以用_来匹配所有可能的值,可以使用let对元组中的值进行值绑定
e.g.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
print("\(somePoint) is at the origin")
case (_, 0):
print("\(somePoint) is on the x-axis")
case (0, _):
print("\(somePoint) is on the y-axis")
case (-2...2, -2...2):
print("\(somePoint) is inside the box")
default:
print("\(somePoint) is outside of the box")
}
// 输出“(1, 1) is inside the box”

let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
print("on the x-axis with an x value of \(x)")
case (0, let y):
print("on the y-axis with a y value of \(y)")
case let (x, y):
print("somewhere else at (\(x), \(y))")
}
// 输出“on the x-axis with an x value of 2”

如果想像C中对case进行贯穿,需要显式调用 fallthrough
e.g.

1
2
3
4
5
6
7
8
9
10
11
let integerToDescribe = 5
var description = "The number \(integerToDescribe) is"
switch integerToDescribe {
case 2, 3, 5, 7, 11, 13, 17, 19:
description += " a prime number, and also"
fallthrough
default:
description += " an integer."
}
print(description)
// 输出“The number 5 is a prime number, and also an integer.”

3.4 循环

for-in循环,while循环以及repeat-while循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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)

var n = 2
while n < 100 {
n *= 2
}
print(n)

var m = 2
repeat {
m *= 2
} while m < 100
print(m)

在for-in循环中,可以使用<.....来表示一个范围,前者右边为开区间,后者两边均为闭区间.

1
2
3
4
5
var total = 0
for i in 0..<4 {
total += i
}
print(total)

使用stride(from:to:by)在for-in中使用step

e.g.

1
2
3
4
let minuteInterval = 5
for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
// 每5分钟渲染一个刻度线(0, 5, 10, 15 ... 45, 50, 55)
}

可以对循环体和条件语句使用标签,从而明确breakcontinue针对的具体代码块.

e.g.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
gameLoop: while square != finalSquare {
diceRoll += 1
if diceRoll == 7 { diceRoll = 1 }
switch square + diceRoll {
case finalSquare:
// 骰子数刚好使玩家移动到最终的方格里,游戏结束。
break gameLoop
case let newSquare where newSquare > finalSquare:
// 骰子数将会使玩家的移动超出最后的方格,那么这种移动是不合法的,玩家需要重新掷骰子
continue gameLoop
default:
// 合法移动,做正常的处理
square += diceRoll
square += board[square]
}
}

if 语句一样,guard的执行取决于一个表达式的布尔值。我们可以使用 guard语句来要求条件必须为真时,以执行 guard语句后的代码。不同于 if 语句,一个 guard语句总是有一个 else 从句,如果条件不为真则执行 else从句中的代码。

在 else 分支上的代码就会被执行。这个分支必须转移控制以退出 guard 语句出现的代码段。它可以用控制转移语句如 return、break、continue 或者 throw 做这件事,或者调用一个不返回的方法或函数,例如 fatalError()

原文链接 http://blog.wuqingzhe.cn/2019/08/13/swift1/