跟OC一样,Swift也是采取基于引用计数的ARC内存管理方案(针对堆空间)
Swift的ARC中有3种引用:
1、强引用(strong reference):默认情况下,引用都是强引用;
2、弱引用(weak reference):通过weak定义弱引用,必须是可选类型的var,因为实例销毁后,ARC会自动将弱引用设置为nil。ARC自动给弱引用设置nil时,不会触发属性观察器;
3、无主引用(unowned reference):通过unowned定义无主引用,不会产生强引用,实例销毁后仍然存储着实例的内存地址(类似于OC中的unsafe_unretained)。试图在实例销毁后访问无主引用,会产生运行时错误(野指针);
注:weak和unowned的区别是,weak在对象销毁后比自动置为nil,unowned不会。正因为weak会被自动置为nil,所以必须是var类型,不能是let(let不可变),同时也必须是可选类型,因为可选类型才能置为nil。这也是为啥在block里用weak后,self?.xxx 访问的缘故。
block中为啥要用self.访问
编译器强制要求在block中访问必须用self.是为了提醒开发者这里有可能会有循环引用的风险,是不是要用weak来修饰。仅仅是为了提醒开发者而做的编译器校验而已。
指针
Swift中也有专门的指针类型,这些都被定性为“Unsafe”(不安全的),常见的有以下4种类型:
1、UnsafePointer<Pointee> 类似于 const Pointee *
2、UnsafeMutablePointer<Pointee> 类似于 Pointee *
3、UnsafeRawPointer 类似于 const void *
4、UnsafeMutableRawPointer 类似于 void *
var age = 10
func test1(_ ptr: UnsafeMutablePointer<Int>) {
ptr.pointee += 10
}
func test2(_ ptr: UnsafePointer<Int>) {
print(ptr.pointee)
}
test2(&age) // 10
test1(&age) // 20
print(age) // 20
var age = 10
func test3(_ ptr: UnsafeMutableRawPointer) {
ptr.storeBytes(of: 20, as: Int.self)
}
func test4(_ ptr: UnsafeRawPointer) {
print(ptr.load(as: Int.self))
}
test4(&age) //10
test3(&age) // 20
print(age) // 20
更多使用例子
var age: Int = 10
var age1: Float = 10.1
func getPtr<T>(ptr: UnsafePointer<T>) -> UnsafePointer<T> {
return ptr
}
let p = getPtr(ptr: &age)
print(p.pointee)
func getPtr1<T>(ptr: UnsafePointer<T>,body:(UnsafePointer<T>) -> T) -> T {
return body(ptr)
}
let p2 = getPtr1(ptr: &age1) { (p) -> Float in
return 10.0
}
print(p2)
func getPtr2<T,Result>(ptr: UnsafePointer<T>,body:(UnsafePointer<T>) -> Result) -> Result {
return body(ptr)
}
let result = getPtr2(ptr: &age) { (p) -> UnsafeRawPointer in
let rp = UnsafeRawPointer(p)
return rp
}
print(result.load(as: Int.self))
class Student {
var age: Int = 20
}
var s: Student = Student()
let studentPtr1 = withUnsafePointer(to: &s){$0}
print(studentPtr1.pointee.age)
// studentPtr是s这个变量的地址值(不是student堆空间的地址值)
let studentPtr = withUnsafePointer(to: &s) {UnsafeRawPointer($0)}
var address = studentPtr.load(as: UInt.self)
// student 堆空间的地址值
let student_dui_ptr = UnsafeMutableRawPointer(bitPattern: address)
print(student_dui_ptr!)
//取出student变量从第16字节开始的数据,也就是age的值。
print(student_dui_ptr?.load(fromByteOffset: 16, as: Int.self))
// 指针往后走16个字节,然后取值
let student_dui_ptr_2 = student_dui_ptr?.advanced(by: 16)
print(student_dui_ptr_2?.load(as: Int.self))
Struct
在swift中,结构体(Struct)是值类型,不是引用类型。一个结构体对象在内存中占据的大小由结构体的成员变量来决定。
值类型赋值给var、let或者给函数传参,是直接将所有内容拷贝一份(深拷贝)
在swift中,Array、Dictionary、String、Set都是结构体,故他们是值类型(这一点和OC不太一样)
struct Point {
var x: Int
var y: Int
}
var p: Point = Point(x: 10, y: 20)
print(MemoryLayout<Point>.size) // 16
print(MemoryLayout<Point>.stride) // 16
print(MemoryLayout<Point>.alignment) // 8
/*
size:表示结构体的内存大小;
stride:表示系统最终分配给他的内存大小;
alignment:表示以多少字节对齐(比如8字节对齐)。
*/
struct Size {
var width: Int
var height: Int
var change: Bool
}
var p: Size = Size(width: 10, height: 20,change: true)
print(MemoryLayout<Size>.size) // 17
print(MemoryLayout<Size>.stride) // 24
print(MemoryLayout<Size>.alignment) // 8
// Size结构体总共需要17个字节(8+8+1),由于是8字节对齐,最终系统会分配24个字节给它
Class
在swift中,类(Class)是引用类型,不是值类型。一个类的对象在内存中占据的大小由类的存储属性大小再加上16个字节。这多出来的16个字节是在最前面的,分别存指向类型的信息(元类)和引用计数器。
引用类型赋值给var、let或者给函数传参,是将内存地址拷贝一份 (浅拷贝/引用拷贝)
class Size {
var width: Int
var height: Int
init(width: Int, height: Int) {
self.width = width
self.height = height
}
}
let pc = Size(width: 1, height: 2)
/* 类的内存布局如下图,在堆空间,占32个字节,前8个字节放指向类型信息,接着8个字节放引用计数器,
剩下的16个字节分别放width和height的值 */
image.png
引用类型不能像值类型那样通过MemoryLayout得到对象的内存大小,因为类的对象是初始化在堆空间的,而指向这个空间的变量有可能是栈空间的地址或全局变量地址。通过MemoryLayout得出的大小永远是8字节(因为一个指针变量就是8字节)。如果要获取引用类型对象的真正堆空间的大小,要用malloc_size函数去获取。代码如下:
class Size {
var width: Int
var height: Int
init(width: Int, height: Int) {
self.width = width
self.height = height
}
}
var pc = Size(width: 39, height: 34)
print(MemoryLayout<Size>.size(ofValue: pc)) // 8
print(MemoryLayout<Size>.stride(ofValue: pc)) // 8
print(MemoryLayout<Size>.alignment) // 8
// 上面的MemoryLayout 的结果永远是8字节
let pr = withUnsafePointer(to: &pc) {UnsafeRawPointer($0)}
// 获得指向pc对象的指针
print(pr.load(as: UInt.self))
// 获得pc对象指向堆空间的地址
let address = UnsafeRawPointer(bitPattern: pr.load(as: UInt.self))
// 获得堆空间的地址转成UnsafeRawPointer指针
print(address)
print(malloc_size(address!)) // 通过这个c函数获取地址大小,结果为32个字节
网友评论