内存管理
- 跟OC一样,Swift也是采用基于引用计数的ARC内存管理方案(针对堆空间)
- Swift的ARC中有3中引用
- 强引用(strong reference):默认情况下,引用都是强引用
- 弱引用(weak reference);通过weak定义弱引用
1)必须是可选类型的var,因为实例销毁后,ARC会自动将弱引用设置为nil
2)ARC自动给弱引用设置nil时,不会触发属性观察器 - 无主引用(unowned reference):通过unowned定义无主引用
1)不会产生强引用,实例销毁后仍然存储着实例的内存地址(类似OC中的unsafe_unretained)
- 视图在实例销毁后访问无主引用会产生运行时错误(野指针)
weak、unowned的使用限制
- weak、unowned只能在类实例上面
Autoreleasepool
class Person {
var age:Int;
var name:String;
init(age:Int,name:String) {
self.age = age;
self.name = name;
}
func run() {
print("myage=\(self.age),name=\(self.name)")
}
deinit {
print("Person deinit")
}
}
autoreleasepool {
let p = Person(age: 12, name: "cyz")
p.run()
}
// myage=12,name=cyz
// Person deinit
循环引用(Reference Cycle)
- weak、unowned都能解决循环引用的问题,unowned要比weak少一些性能消耗
- 在生命周期中可能会变为nil的使用weak
- 初始化赋值后再也不会变为nil的使用unowned
闭包的循环引用
- 闭包表达式默认会对用到的外层对象产生额外的强引用(对外层对象进行了retain操作)
- 下面代码是循环引用的例子,(如果不用weak或者unowned修饰的话就看不到deinit被调用)
lass Person {
var fn:(()->())?
func run() {
print("run")
}
deinit {
print("Person deinit")
}
}
func test() {
let p = Person()
// p.fn = {p.run()} // 会引起循环引用,deinit不会被调用
// p.fn = {
// [weak p] in
// p?.run()
// }
// p.fn = {
// [unowned p] in
// p.run()
// }
p.fn = {
[weak wp = p, unowned up = p, a = 10 + 2] in
wp?.run()
print("a=\(a)")
}
p.fn?()
}
test()
// 输出为
// run
// a=12
// Person deinit
闭包的循环引用
- 如果现在定义闭包属性的同时引用self,这个闭包必须是lazy的(因为实例初始化完毕之后才能引用self)
- 以下的闭包fn内部如果用到了实例成员(属性、方法),编译器会强制要求明确写出self
class Person {
lazy var fn:(()->()) = {
[weak self] in
self?.run()
}
func run() {
print("run")
}
deinit {
print("Person deinit")
}
}
- 如果lazy属性是闭包调用的结果,那么不用考虑循环引用的问题(因为闭包调用之后,闭包的声明周期就结束了)
var age:Int = 0
lazy var getAge:Int = {
self.age
}()
@escaping
- 非逃逸闭包、逃逸闭包,一般都是当做参数传递给函数
- 非逃逸闭包:闭包调用发生在函数结束前,闭包调用在函数作用域内
- 逃逸闭包:闭包有可能在函数结束后调用,闭包调用逃离了函数的作用域
typealias Fn = () -> ()
// fn是非逃逸闭包,闭包在函数内执行
func test1(_ fn:Fn) {
fn()
}
func test3(_ fn: @escaping Fn) {
// 异步执行,会在函数调用完成之后执行闭包
DispatchQueue.global().async {
fn()
}
}
class Person {
var fn:Fn
init(fn:@escaping Fn) {
self.fn = fn
}
func run() {
// DispatchQueue.global().async本身也是一个逃逸闭包
// 它用到了实例成员(属性、方法),编译器会强制要求明确写出self
DispatchQueue.global().async {
self.fn()
}
}
}
- 逃逸闭包不能捕获inout参数
内存访问冲突(Conflicting Access to Memory)
- 内存访问冲突会在两个访问满足下列条件时发生:
- 至少一个是写入操作
- 他们访问的是同一块内存
- 他们的访问时间叠加(比如在同一函数内)
- 如果下面的条件满足,就说明重叠访问的属性是安全的
- 你只访问实例存储属性,不是计算属性或者类属性
- 结构体是局部变量而非全局变量
- 结构体要么没有被闭包捕获要么只被非逃逸闭包捕获
指针
- Swift中也有专门的指针类型,这些都被定性为“Unsafe”不安全的
- UnsafePointer<Pointee> 类似于const Pointee *
- UnsafeMutablePointer<Pointee> 类似于Pointee *
- UnsafeRawPointer 类似于 const void *
- UnsafeMutableRawPointer 类似于 void *
var age = 10
func test1(_ ptr:UnsafeMutablePointer<Int>) {
ptr.pointee += 10
}
func test2(_ ptr: UnsafePointer<Int>) {
print(ptr.pointee)
}
func test3(_ ptr:UnsafeMutableRawPointer) {
ptr.storeBytes(of: 20, as: Int.self)
}
func test4(_ ptr: UnsafeRawPointer) {
print(ptr.load(as: Int.self))
}
test3(&age)
test4(&age) // 20
print("print age=\(age)") // 20
指针的应用示例
var arr = NSArray(objects: 11,22,33,44)
arr.enumerateObjects { (obj, idx, stop) in
print(idx,obj)
if idx == 2 {
stop.pointee = true
}
}
for (idx,obj) in arr.enumerated() {
print(idx,obj)
if idx == 2 {
break
}
}
// 输出
// 0 11
// 1 22
// 2 33
字面量
var age = 10
var isBlue = true
var name = "hahaha"
- 以上代码的10、true、"hahaha"就是字面量
- 常见的字面量的默认类型
public typealias IntegerLiteralType = Int
public typealias FloatLiteralType = Double
public typealias BooleanLiteralType = Bool
public typealias StringLiteralType = String
- Swift自带的绝大部分类型,都支持通过字面量进行初始化
String、Int、Float、Double、Array、Dictionary、Set、Optional等
可以通过typealias修改字面量的默认类型
typealias FloatLiteralType = Float
typealias IntegerLiteralType = UInt8
var height = 10.1
var age = 10
print(type(of: height)) //Float
print(type(of: age)) // Float
字面量协议
- Swift自带类型之所以能够通过字面量初始化,是因为他们遵守了对应的协议
Int: ExpressibleByIntegerLiteral
Dictionary : ExpressibleByDictionaryLiteral
...
字面量协议应用
// 让Int遵循Bool协议,就能吧bool值赋值给Int数据类型
extension Int : ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = value ? 1 : 0
}
}
var num : Int = true
print(num) // 1
class Student: ExpressibleByIntegerLiteral,ExpressibleByFloatLiteral,ExpressibleByStringLiteral,CustomStringConvertible {
var name : String = ""
var score : Double = 0
required init(floatLiteral value: Double) {
self.score = value
}
required init(integerLiteral value: Int) {
self.score = Double(value)
}
required init(stringLiteral value: String) {
self.name = value
}
required init(unicodeScalarLiteral value: String) {
self.name = value
}
required init(extendedGraphemeClusterLiteral value: String) {
self.name = value
}
var description: String {
"name=\(name),score=\(score)"
}
}
var stu : Student = 90
print(stu) // name=,score=90.0
stu = 96.9
print(stu) // name=,score=96.9
stu = "Jack"
print(stu) // name=Jack,score=0.0
struct Point {
var x = 0.0,y = 0.0
}
extension Point : ExpressibleByArrayLiteral, ExpressibleByDictionaryLiteral {
init(arrayLiteral elements: Double...) {
guard elements.count > 0 else {
return
}
self.x = elements[0]
guard elements.count > 1 else {
return
}
self.y = elements[1]
}
init(dictionaryLiteral elements: (String,Double)...) {
for (k,v) in elements {
if k == "x" {
self.x = v
}else if k == "y" {
self.y = v
}
}
}
}
var p:Point = [10.5,20.5,30.5]
print(p) // Point(x: 10.5, y: 20.5)
p = ["x":11,"y":22]
print(p) // Point(x: 11.0, y: 22.0)
模式匹配
通配符模式(Wildcard Pattern)
- _ 匹配任何值
- _? 匹配非nil值 例如:
enum Life {
case human(name:String, age:Int?)
case animal(name:String, age:Int?)
}
func check(_ life:Life) {
switch life {
case .animal(name: let name, age: _?):
print("animal",name)
case .human(name: let name, age: _):
print("human",name)
default:
print("不符合以上两个条件 other")
}
}
check(.human(name: "rose", age: 20))
check(.human(name: "jack", age: nil))
check(.animal(name: "dog", age: 5))
check(.animal(name: "Cat", age: nil))
输出值:
human rose
human jack
animal dog
不符合以上两个条件 other
最后一个check(.animal(name: "Cat", age: nil))
age是nil而case中指定age不能为空才满足条件,所以会输出到default
标识符模式(Identifier Pattern)
- 给对应的变量、常量名赋值
var age = 10
let name = "jack"
值绑定模式(Value-Binding Pattern)
let point = (3,2)
switch point {
case let (x,y):
print("The point is at (\(x),\(y))")
}
// 输出结果:
// The point is at (3,2)
元组模式(Tuple Pattern)
let points = [(0,0),(1,0),(2,1)]
for (x,_) in points {
print(x)
}
// 输出结果:
// 0
// 1
// 2
let scores = ["jack":90,"rose":80,"kate":70]
for (name,score) in scores {
print(name,score)
}
// 字典是无序的,
// rose 80
// kate 70
// jack 90
let ages:[Int?] = [nil,2,3,nil,5]
for case let age? in ages {
print(age)
}
// 2
// 3
// 5
func checkNum(_ num:Int?) {
switch num {
case 2?: print("2")
case 4?: print("4")
case 6?: print("6")
case _?:print("other 非空")
case _:print("nil 当前为空值")
}
}
checkNum(4)
checkNum(6)
checkNum(3)
checkNum(9)
checkNum(nil)
// 4
// 6
// other 非空
// other 非空
// nil 当前为空值
类型转换模式(Type-Casting Pattern)
let num: Any = 6
switch num {
case is Int:
// 编译器依然认为num是Any类型
print("is Int",num)
default:
break
}
// is Int 6
表达式模式(Expression Pattern)
- 表达式模式用case中
func checkPoint(_ point:(Int,Int)) {
switch point {
case (0,0):
print("(0,0) is at the origin")
case (-2...2,-2...2):
print("(\(point.0),\(point.1)) is near the origin")
default:
print("The point is at(\(point.0),\(point.1))")
}
}
checkPoint((1,2))
checkPoint((0,0))
checkPoint((3,4))
// (1,2) is near the origin
// (0,0) is at the origin
// The point is at(3,4)
自定义表达式模式
struct Student {
var score = 0,name = ""
static func ~= (pattern:Int, value:Student) -> Bool {value.score >= pattern}
static func ~= (pattern: ClosedRange<Int>,value:Student) -> Bool {pattern.contains(value.score)}
static func ~= (pattern: Range<Int>, value: Student) -> Bool {pattern.contains(value.score)}
}
var stu = Student(score: 75, name: "jack")
switch stu {
case 100:print(">=100")
case 90:print(">=90 100<=")
case 80..<90: print("[80, 90)")
case 60...80:print("[60 80)")
case 0:print(">=0")
default:
break
}
// [60 80)