Swift基础语法3(内存管理、字面量、模式匹配)

内存管理

  • 跟OC一样,Swift也是采用基于引用计数的ARC内存管理方案(针对堆空间)
  • Swift的ARC中有3中引用
  1. 强引用(strong reference):默认情况下,引用都是强引用
  2. 弱引用(weak reference);通过weak定义弱引用
    1)必须是可选类型的var,因为实例销毁后,ARC会自动将弱引用设置为nil
    2)ARC自动给弱引用设置nil时,不会触发属性观察器
  3. 无主引用(unowned reference):通过unowned定义无主引用
    1)不会产生强引用,实例销毁后仍然存储着实例的内存地址(类似OC中的unsafe_unretained)
  1. 视图在实例销毁后访问无主引用会产生运行时错误(野指针)

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少一些性能消耗
  1. 在生命周期中可能会变为nil的使用weak
  2. 初始化赋值后再也不会变为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)

  • 内存访问冲突会在两个访问满足下列条件时发生:
  1. 至少一个是写入操作
  2. 他们访问的是同一块内存
  3. 他们的访问时间叠加(比如在同一函数内)

  • 如果下面的条件满足,就说明重叠访问的属性是安全的
  1. 你只访问实例存储属性,不是计算属性或者类属性
  2. 结构体是局部变量而非全局变量
  3. 结构体要么没有被闭包捕获要么只被非逃逸闭包捕获

指针

  • 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自带的绝大部分类型,都支持通过字面量进行初始化
StringIntFloatDoubleArrayDictionarySetOptional

可以通过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)

分类:
iOS
标签: