Tuple, Swap two items
func mySwap<T>(_ left: inout T, _ right: inout T) {
(left, right) = (right, left)
}
let a: Int = 1
let b: Int = 2
mySwap(a, b)
@escaping
使⽤闭包的⽅式来传递这个参
数是常见⼿段:
func doWork(block: ()->()) {
block()
}
doWork {
print("work")
}
这种最简单的形式的闭包其实还默认隐藏了⼀个假设,那就是参数中 block 的内容会在 doWork
返回前就完成。也就是说,对于 block 的调⽤是同步⾏为。如果我们改变⼀下代码,将 block 放
到⼀个 Dispatch 中去,让它在 doWork 返回后被调⽤的话,我们就需要在 block 的类型前加上
@escaping 标记来表明这个闭包是会“逃逸”出该⽅法的:
func doWorkAsync(block: @escaping ()->()) {
DispatchQueue.main.async {
block()
}
}
- capturing value
- 对于 doWork 参数⾥这样的没有逃逸⾏为的闭包,因为闭包的作⽤域不会超过函数本⾝,所 以我们不需要担⼼在闭包内持有 self 等。
⽽接受 @escaping 的 doWorkAsync 则有所不同。由于需
要确保闭包内的成员依然有效,如果在闭包内引⽤了 self 及其成员的话,Swift 将强制我们明确
地写出 self如果我们不希望在闭包中持有 self ,可以使⽤ [weak
self] 的⽅式来表达:
func method3() {
doWorkAsync { [weak self] _ in
print(self?.foo)
}
foo = "bar"
}
method3() // nil
如果在协议或者父类重定义了一个接受@escaping为参数方法,
那么在实现协议和类型或者是这个父类的子类中,对应的方法也必须被声明为@escaping,
否则两个方法会被认为拥有不同的函数签名
protocol P {
func work(b: @escaping ()->())
}
// 可以编译
class C: P {
func work(b: @escaping () -> ()) {
DispatchQueue.main.async {
print("in C")
b()
}
}
}
// ⽽这样是⽆法编译通过的:
class C1: P {
func work(b: () -> ()) {
// ...
}
}
Overload Operator
overload an operator, that swift has existed operator
struct Vector2D {
var x = 0.0
var y = 0.0
}
func +(left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y + right.y)
}
let v1 = Vector2D(x: 2.0, y: 3.0)
let v2 = Vector2D(x: 1.0, y: 4.0)
let v4 = v1 + v2
// v4 为 {x 3.0, y 7.0}
overload a new operator, that swift did not contain
- declare the new operator
precedencegroup DotProductPrecedence {
associativity: none
higherThan: MultiplicationPrecedence
}
infix operator +*: DotProductPrecedence
// precedence group - define the priority
// associativity: - define the 结合律\(Associativity\),
// 即如果多个同类的操作符顺序出现的计算顺序。
// ⽐如常见的加法和减法都是 left ,就是说多个加法同时出现时按照从左往右的顺序计算
// higherThan: - 运算的优先级,
// 点积运算是优先于乘法运算的。除了 higherThan ,也⽀持使⽤ lowerThan 来指定优先级低于某个其他组。
// infix - 表⽰要定义的是⼀个中位操作符,即前后都是输⼊;其他的修饰⼦还包括 prefix 和 postfix
// let result = v1 +* v2
// 输出为 14.0
func +* (left: Vector2D, right: Vector2D) -> Double {
return left.x * right.x + left.y * right.y
}
ISSUE: Swift 的操作符是不能定义在局部域中的,
因为⾄少会希望在能在全局范围使⽤你的操作符,否则操作符也就失去意义了。
另外,来⾃不同 module 的操作符是有可能冲突的。
如果库中的操作符冲突的话,使⽤者是⽆法通过指定库名字来进⾏调⽤的。
func 的参数修饰
- default passed value in func is let
// default condition
// let can be omitted
func incrementor(variable: let Int) -> Int {
print(variable)
return variable
}
if we want to modify the passed value
// after Swift 2.2, we cannot replace let to var directly // use another "var" to explicit "let variable", // the another "var" can be modified func incrementor(variable: Int) -> Int { var num = variable num += 1 return num }
if we want to pass the reference
func incrementor\(variable: inout Int) { variable += 1 } var luckyNumber = 7 incrementor(variable: &luckyNumber) print(luckyNumber) // luckyNumber = 8
参数的修饰是具有传递限制的,对于跨越层级的调⽤,需要保证同⼀参数的修饰是统⼀的。
举个例⼦,⽐如我们想扩展⼀下上⾯的⽅法,实现⼀个可以累加任意数字的 +N器 的话,可以写成这样:func makeIncrementor(addNumber: Int) -> ((inout Int) -> ()) { func incrementor(inout variable: Int) -> () { variable += addNumber } return incrementor; }
literal value expressing 字面量表达
- 就是指像特定的数字,字符串或者是布尔值这样,能够直截了当地指出⾃⼰的类型 并为变量进⾏赋值的值。⽐如在下⾯:
let aNumber = 3
let aString = "Hello"
let aBool = true
let anArray = [1,2,3]
let aDictionary = ["key1": "value1", "key2": "value2"]
// 其中的 3 , Hello 以及 true 就称为字⾯量。
- Swift provide several protocols
ExpressibleByArrayLiteral ExpressibleByBooleanLiteral ExpressibleByDictionaryLiteral ExpressibleByFloatLiteral ExpressibleByNilLiteral ExpressibleByIntegerLiteral ExpressibleByStringLiteral ExpressibleByExtendedGraphemeClusterLiteral ExpressibleByUnicodeScalarLiteral // 所有的字⾯量表达协议都定义了⼀个 typealias 和对应的 init ⽅法
Example
protocol ExpressibleByBooleanLiteral {
typealias BooleanLiteralType
/// Create an instance initialized to `value`.
init(booleanLiteral value: BooleanLiteralType)
}
/// in this protocol, BooleanLiteralType 在 Swift 标准库中已经有定义了:
/// The default type for an otherwise-unconstrained boolean literal
typealias BooleanLiteralType = Bool
enum MyBool: Int {
case myTrue, myFalse
}
extension MyBool: ExpressibleByBooleanLiteral {
init(booleanLiteral value: Bool) {
self = value ? .myTrue : .myFalse
}
}
// we can directly use the Bool's true and false to init the enum
let myTrue: MyBool = true
let myFalse: MyBool = false
myTrue.rawValue // 0
myFalse.rawValue // 1
Preventing Overrides
- final: prevent a method, property, or subscript from being overridden. such as final var, final func, final class func, and final subscript).
- with final, any attempt to override a final method/property/subscript in a subclass is reported as a compile-time error.
- final class className, any attempt to subclass a final class is reported as a compile-time error.
- extension can also be marked as final within the extension’s definition.
Identity Operators - only for class
Swift provides two identity operators:
Identical to (===)
Not identical to (!==)
/// check two constants or variables refer to the same single instance:
let tenEighty = VideoMode()
let alsoTenEighty = tenEighty
if tenEighty === alsoTenEighty {
print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// Prints "tenEighty and alsoTenEighty refer to the same VideoMode instance."
Note
“identical to” (represented by three equals signs, or ===) does not mean the same thing as “equal to” (represented by two equals signs, or ==):
- “Identical to” means that two constants or variables of class type refer to exactly the same class instance.
- “Equal to” means that two instances are considered “equal” or “equivalent” in value, for some appropriate meaning of “equal”, as defined by the type’s designer.
random
最佳实践当然是为创建⼀个 Range 的随机数的⽅法,这样我们就能在之后很容易地复⽤,甚⾄设
计类似与 Randomable 这样的协议了:
func random(in range: Range<Int>) -> Int {
let count = UInt32(range.endIndex - range.startIndex)
return Int(arc4random_uniform(count)) + range.startIndex
}
for _ in 0...100 {
let range = Range<Int>(1...6)
print(random(in: range))
}