Swift5.x入門17--訪問控制,內(nèi)存管理跪腹,指針

訪問控制

  • 在訪問權(quán)限控制這塊褂删,Swift提供了5個(gè)不同的訪問級別,以下是從高到低的敘述冲茸,其中實(shí)體是指被訪問級別修飾的內(nèi)容屯阀;

  • open:允許在定義實(shí)體的模塊缅帘,其他模塊中訪問,允許其他模塊進(jìn)行繼承难衰,重寫股毫,open只能用在類class,類成員上召衔,不允許用在枚舉與結(jié)構(gòu)體上铃诬,這里的模塊是指工程編譯生成的Mach-O可執(zhí)行文件

  • public:允許在定義實(shí)體的模塊苍凛,其他模塊中訪問趣席,不允許其他模塊進(jìn)行繼承,重寫醇蝴,這里的模塊同上宣肚;

    image.png

  • internal:只允許在定義實(shí)體的模塊中訪問,不允許其他模塊訪問悠栓,這里的模塊是指子模塊霉涨,如下圖所示:

    image.png

  • fileprivate:只允許在定義實(shí)體的源文件中訪問,即在指定的.swift文件中訪問惭适,在其他.swift文件中不能訪問笙瑟;

  • private:只允許在定義實(shí)體的封閉聲明中訪問,例如在一個(gè).swift文件中定義A癞志,B兩個(gè)Class類往枷,那么A類中定義的實(shí)體只能在A類中訪問,不能在B類中訪問凄杯;

  • 絕大部分實(shí)體默認(rèn)都是internal級別错洁;

訪問級別的使用準(zhǔn)則

  • 一個(gè)實(shí)體不可以被更低訪問級別的實(shí)體定義;
    • 變量類型的訪問級別 大于等于 變量的訪問級別戒突;
    • 參數(shù)類型/返回值類型的訪問級別 大于等于 函數(shù)的訪問級別屯碴;
    • 父類的訪問級別 大于等于子類的訪問級別;
    • 父協(xié)議的訪問級別 大于等于 子協(xié)議的訪問級別膊存;
    • 原類型的訪問級別 大于等于 typealias的訪問級別;
    • 原始值导而,關(guān)聯(lián)值類型的訪問級別 大于等于 枚舉的訪問級別;
    • 定義類型A所用到的其他類型的訪問級別 大于等于 A的訪問級別膝舅;

元組類型

  • 元組類型的訪問級別是其所有成員類型最低的那個(gè)嗡载;
internal struct Dog { }
fileprivate class Person { }

fileprivate var data1: (Dog,Person)
private var data2: (Dog,Person)
var data3: (Dog,Person) //報(bào)錯(cuò)
  • (Dog,Person)是元組類型,其中Dog的訪問級別> Person的訪問級別仍稀,所以(Dog,Person)元組的訪問級別為fileprivate
  • 現(xiàn)在用元組類型來定義變量,那么元組類型的訪問級別必須
    大于等于變量data1埂息,data2技潘,data3的訪問級別遥巴,data3默認(rèn)的訪問級別為internal,所以報(bào)錯(cuò)享幽;

泛型

  • 泛型類型的訪問級別是類型的訪問級別以及所有泛型類型參數(shù)的訪問級別中最低的那個(gè)铲掐;
internal class Car { }
fileprivate class Dog {}
public class Person<T1, T2> { }

fileprivate var p = Person<Car,Dog>()
  • < >中的內(nèi)容表示泛型,其訪問級別是Person值桩,Car摆霉,Dog這三個(gè)訪問級別中最低的那個(gè),所以泛型的訪問級別為fileprivate奔坟;

成員携栋,嵌套類型

  • 類型的訪問級別會(huì)影響成員(方法,屬性咳秉,下標(biāo)婉支,初始化器),嵌套類型的默認(rèn)訪問級別澜建;
  • 一般情況下向挖,類型為privatefileprivate,那么成員炕舵,嵌套類型默認(rèn)也為privatefileprivate
  • 一般情況下何之,類型為internalpublic,那么成員咽筋,嵌套類型默認(rèn)是internal帝美,
fileprivate class Person {
    var age = 0
    func run() { }
    
    enum Season {
        case spring,summer,autum,winter
    }
}
  • 類型為fileprivate,那么其所有成員晤硕,也為fileprivate
public class Person {
    var age = 0
    func run() { }
    
    enum Season {
        case spring,summer,autum,winter
    }
}
  • 類型為public悼潭,其所有成員為internal

在main.swift文件中直接定義 代碼如下:

import Foundation
private class Person { }
fileprivate class Student : Person { }
  • 根據(jù)上面的規(guī)則,父類的訪問級別要大于等于子類的訪問級別舞箍,按理說會(huì)報(bào)錯(cuò)舰褪,但編譯成功,原因在于 PersonStudent疏橄,是在main.swift源文件的直接定義的占拍,在整個(gè)main.swift源文件中都可以訪問,如果做如下改動(dòng):
import Foundation
class TestClass {
    private class Person { }
    fileprivate class Student : Person { }
}
  • 就會(huì)報(bào)錯(cuò)捎迫,因?yàn)镻erson的作用域僅局限于TestClass的內(nèi)部晃酒,Student是在整個(gè)main.swift源文件中都可以訪問,若在main.swift源文件中直接實(shí)例Student窄绒,就不能訪問其父類Person贝次,所以會(huì)報(bào)錯(cuò);
class Test {
    private struct Dog {
        var age: Int = 0
        func run(){}
    }
    
    private struct Person {
        var dog: Dog = Dog()
        mutating func walk() {
            dog.run()
            dog.age = 1
        }
    }
}
  • 編譯通過彰导,根據(jù)上面的規(guī)則蛔翅,private struct Dog 敲茄,那么其成員age與run的訪問級別也為private,然而dog.run山析,依然能在Person內(nèi)部使用堰燎,原因在于成員age與run的訪問級別的private是與類Dog的private的作用域是一致的,若改成下面的:
class Test {
    private struct Dog {
        private var age: Int = 0
        private func run(){}
    }
    
    private struct Person {
        var dog: Dog = Dog()
        mutating func walk() {
            dog.run()
            dog.age = 1
        }
    }
}
  • 編譯報(bào)錯(cuò)笋轨,private func run(){}秆剪,不能在Person中使用;

成員的重寫

  • 子類重寫成員的訪問級別必須 >= 子類的訪問級別 或者 >= 父類被重寫成員的訪問級別爵政;
public class Person {
    fileprivate func run() {
        
    }
}

public class Student : Person {
    fileprivate override func run() {
        
    }
}

getter仅讽,setter

  • getter,setter默認(rèn)自動(dòng)接收它們所屬環(huán)境的訪問級別茂卦;
  • 可以給setter單獨(dú)設(shè)置一個(gè)比getter更低的訪問級別何什,用以限制寫的權(quán)限;
class Person {
    private(set) var age: Int = 0
    fileprivate(set) public var weight: Int {
        set {
            
        }
        get {
            10
        }
    }
    internal(set) public subscript(index: Int) -> Int {
        set {
            
        }
        get {
            index
        }
    }
}

var p: Person = Person()
p.age = 10 //不能寫等龙,報(bào)錯(cuò)
print(p.age)

初始化器

  • 如果一個(gè)public類想在另一個(gè)模塊調(diào)用編譯生成的默認(rèn)無參初始化器处渣,必須顯示提供public的無參初始化器, 因?yàn)?code>public類的默認(rèn)初始化器是internal級別蛛砰;
  • required初始化器 >= 它的默認(rèn)訪問級別
class Person {
    fileprivate required init() {}
     
}
  • 會(huì)報(bào)錯(cuò)罐栈,因?yàn)?code>class Person的默認(rèn)訪問級別為internal,根據(jù)上面的規(guī)則:required初始化器 >= 它的默認(rèn)訪問級別泥畅,而現(xiàn)在required初始化器的訪問級別為fileprivate < internal荠诬,所以報(bào)錯(cuò);

  • 如果結(jié)構(gòu)體有private/fileprivate的存儲(chǔ)屬性位仁,那么它的成員初始化器也是private/fileprivate柑贞,默認(rèn)為internal

struct Point {
    fileprivate var x = 0
    var y = 0
}

var p: Point = Point(x: 10, y: 20)

枚舉類型的case

  • 不能給enum的每個(gè)case設(shè)置訪問級別;
  • 每個(gè)case自動(dòng)接收enum的訪問級別聂抢;
public enum Season {
    case spring,summer,autum,winter
}
  • enum是public
  • 所有的case也都是public

協(xié)議

  • 協(xié)議中定義的要求自動(dòng)接收協(xié)議的訪問級別钧嘶,不能單獨(dú)設(shè)置訪問級別,跟枚舉類似琳疏;
  • 協(xié)議實(shí)現(xiàn)的訪問級別 >= 類型的訪問級別 或者 >= 協(xié)議的訪問級別
//協(xié)議的訪問級別public
public protocol Runnable {
    func run()
}
//類型的訪問級別fileprivate
fileprivate class Person : Runnable{
    //協(xié)議實(shí)現(xiàn)的訪問級別internal
    internal func run() {
        
    }
}

擴(kuò)展

  • 如果有顯示設(shè)置擴(kuò)展的訪問級別有决,擴(kuò)展添加的成員自動(dòng)接收擴(kuò)展的訪問級別;
  • 如果沒有顯示設(shè)置擴(kuò)展的訪問級別空盼,擴(kuò)展添加的成員的默認(rèn)訪問級別與直接在類型中定義的成員一樣书幕;
  • 可單獨(dú)給擴(kuò)展的成員設(shè)置訪問級別;
  • 不能給用于遵守協(xié)議的擴(kuò)展顯示設(shè)置擴(kuò)展的訪問級別揽趾;
protocol Runnable {
    func run()
}

class Person{
    
}

fileprivate extension Person : Runnable { //報(bào)錯(cuò)
    
}
  • 在同一文件中的擴(kuò)展台汇,可以寫成類似多個(gè)部分的類型聲明;
  • 在原本的聲明中定義一個(gè)私有成員,可以在同一文件的擴(kuò)展中訪問它励七;
  • 在擴(kuò)展中定義一個(gè)私有成員智袭,可以在同一文件的其他擴(kuò)展中奔缠,原本聲明中訪問它尉桩;
public class Person {
    private func run0() {}
    private func eat0() {
        run1()
    }
}

extension Person {
    private func run1() {}
    private func eat1() {
        run0()
    }
}

extension Person {
    private func eat2() {
        run1()
    }
}

將方法賦值給let或者var

struct Person {
    var age: Int
    func run(_ v: Int) {
        print("run",age,v)
    }
    
    static func run(_ v: Int) {
        print("static run",v)
    }
}

var fn1 = Person.run
fn1(20)

var fn2: (Person) -> (Int) -> () = Person.run
fn2(Person(age: 10))(20)
struct Person {
    var age: Int
    func run(_ v: Int) {
        print("run",age,v)
    }
    
    static func run(_ v: Int) {
        print("static run",v)
    }
}

//(Person) -> (Int) -> ()
var fn = Person.run
//fn1是帶有Person實(shí)例的run方法
//(Int) -> ()
var fn1 = fn(Person(age: 10))
//最后傳入?yún)?shù)20珊豹,調(diào)用run方法
fn1(20)

內(nèi)存管理

  • 與OC一樣,Swift也是采取基于引用計(jì)數(shù)的ARC內(nèi)存管理方案;
  • Swift的ARC中有3種引用:
    • 強(qiáng)引用:默認(rèn)情況下伍绳,都是強(qiáng)引用;
    • 弱引用:通過weak來定義弱引用站楚,必須是可選類型的var蝠引,因?yàn)閷?shí)例銷毀后,ARC會(huì)自動(dòng)將若引用置為nil抱怔;ARC自動(dòng)給弱引用設(shè)置為nil時(shí)劣坊,是不會(huì)觸發(fā)屬性觀察器的;
  • 無主引用(unowned reference):通過unowned定義無主引用屈留,其不會(huì)產(chǎn)生強(qiáng)引用局冰,實(shí)例對象銷毀之后引用依然指向?qū)嵗膬?nèi)存地址,類似于OC中的unsafe_unretained灌危,試圖在實(shí)例對象銷毀后康二,訪問無主引用,會(huì)導(dǎo)致野指針錯(cuò)誤勇蝙;
  • weakunowned只能應(yīng)用于類的實(shí)例上面沫勿;
protocol Liveable : AnyObject {
    
}

class Person {
    
}
 
weak var p0: Person?
weak var p1: AnyObject?
weak var p2: Liveable?

unowned var p3: Person?
unowned var p4: AnyObject?
unowned var p5: Liveable?

AutoRelease

循環(huán)引用

  • weakunowned都能解決循環(huán)引用的問題,但unowned要比weak少一些性能消耗味混;
  • 在實(shí)例對象的生命周期中會(huì)變成nil的产雹,使用weak
  • 實(shí)例對象初始化賦值之后,再也不會(huì)變成nil的翁锡,使用unowned

閉包的循環(huán)引用

  • 閉包表達(dá)式默認(rèn)會(huì)對用到的外層對象產(chǎn)生額外的強(qiáng)引用(即對外層對象進(jìn)行了retain操作)
class Person {
    var fn: (() -> ())?
    func run() {
        print("Person run")
    }
    deinit {
        print("Person deinit")
    }
}

func test() {
    let p: Person = Person()
    p.fn = {
        p.run()
    }
}

test()
  • 閉包fn蔓挖,會(huì)對p進(jìn)行強(qiáng)引用,導(dǎo)致p無法被釋放盗誊,造成內(nèi)存泄漏时甚;
  • 解決方案:在閉包表達(dá)式的捕獲列表聲明weakunowned引用,解決循環(huán)引用問題哈踱;
class Person {
    var fn: (() -> ())?
    func run() {
        print("Person run")
    }
    deinit {
        print("Person deinit")
    }
}

func test() {
    let p: Person = Person()
    p.fn = {
        [weak p] in
        p?.run()
    }
}

test()
  • 或者
class Person {
    var fn: (() -> ())?
    func run() {
        print("Person run")
    }
    deinit {
        print("Person deinit")
    }
}

func test() {
    let p: Person = Person()
    p.fn = {
        [unowned p] in
        p.run()
    }
}

test()
  • 或者
class Person {
    var fn: (() -> ())?
    func run() {
        print("Person run")
    }
    deinit {
        print("Person deinit")
    }
}

func test() {
    let p: Person = Person()
    p.fn = {
        [weak wp = p] in
        wp?.run()
    }
}

test()
  • 先看一個(gè)實(shí)例代碼:
class Person {
    lazy var fn: (() -> ()) = {
        [weak weakSelf = self] in
        weakSelf?.run()
    }
    func run() {
        print("Person run")
    }
    deinit {
        print("Person deinit")
    }
}

func test() {
    var p: Person = Person()
    p.fn()
}

test()
  • 上述閉包內(nèi)部用到實(shí)例的成員荒适,必須強(qiáng)制寫上self,否則會(huì)報(bào)錯(cuò)开镣,因?yàn)檫@樣可以提醒開發(fā)者刀诬,可能會(huì)造成循環(huán)引用;
  • lazy var fn: (() -> ()) = { }邪财,只有使用到fn陕壹,才會(huì)去初始化加載fn质欲,也就是說調(diào)用p.fn(),才會(huì)初始化fn糠馆,可能會(huì)造成循環(huán)引用嘶伟;
  • 再看一段代碼:
class Person {
    var age: Int = 0
    lazy var getAge: Int = {
        self.age
    }()
    deinit {
        print("Person deinit")
    }
}

func test() {
    var p: Person = Person()
    print(p.getAge)
}

test()
  • 如果將lazy刪除,self.age會(huì)報(bào)錯(cuò)又碌,這是因?yàn)閟elf實(shí)例還沒初始化九昧,就調(diào)用getAge,若加上lazy毕匀,只有當(dāng)實(shí)例初始化后才去調(diào)用getAge铸鹰,就不會(huì)報(bào)錯(cuò)了;
  • 如果lazy屬性是閉包調(diào)用的結(jié)果皂岔,那么就不用考慮循環(huán)引用問題蹋笼,因?yàn)殚]包調(diào)用后,閉包的生命周期就結(jié)束了躁垛;

@escaping

  • 非逃逸閉包剖毯,逃逸閉包,一般都是當(dāng)作參數(shù)傳遞給函數(shù)缤苫;
  • 非逃逸閉包:閉包調(diào)用發(fā)生在函數(shù)結(jié)束之前速兔,閉包作用域在函數(shù)之內(nèi);
func test(_ fn: () -> ()) {
    fn()
}

test {
    print("1111")
}
  • fn是非逃逸閉包活玲,其調(diào)用在test函數(shù)內(nèi)部涣狗;
  • 逃逸閉包:閉包有可能在函數(shù)結(jié)束后調(diào)用,閉包調(diào)用逃離了函數(shù)的作用域舒憾,需要通過@escaping進(jìn)行聲明镀钓;
import Dispatch

typealias Fn = () -> ()

var gFn: Fn?

func test1(_ fn: @escaping Fn) {
    gFn = fn
}

func test2(_ fn: @escaping Fn) {
    DispatchQueue.global().async {
        fn()
    }
}
  • fn是逃逸閉包,其調(diào)用不在test1镀迂,test2的函數(shù)內(nèi)部丁溅;
  • 逃逸閉包是不能捕獲inout類型的參數(shù)的,因?yàn)樘右蓍]包的調(diào)用是不確定的探遵,而inout類型的參數(shù)是傳入變量的內(nèi)存地址給逃逸閉包窟赏,有可能變量已經(jīng)銷毀了,逃逸閉包再去修改變量所指向的內(nèi)存箱季;

局部作用域

  • 在Swift中用do { }定義局部作用域涯穷;
class Dog {
    var age: Int = 10
    func run() {
        
    }
}

do {
    let dog1: Dog = Dog()
    dog1.age = 20
}

do {
    let dog2: Dog = Dog()
    dog2.age = 20
}
  • dog1在第一個(gè)do{}作用域內(nèi),dog2在第二個(gè)do{}作用域內(nèi)藏雏,超出作用域拷况,就會(huì)被釋放回收;

內(nèi)存訪問沖突

  • 既然是內(nèi)存訪問沖突,說明至少有兩個(gè)訪問赚瘦,訪問同一資源粟誓;
  • 內(nèi)存訪問沖突的發(fā)生條件:
    • 兩個(gè)訪問,至少有一個(gè)寫入操作起意;
    • 兩個(gè)訪問鹰服,訪問的是同一塊內(nèi)存;
    • 兩個(gè)訪問的訪問時(shí)間重疊(比如在同一個(gè)函數(shù)內(nèi))
  • 看下面一段代碼:
var step = 1

func increment(_ num: inout Int) {
    num += step
}

increment(&step)
  • 報(bào)錯(cuò):Simultaneous accesses to 0x100008020, but modification requires exclusive access
  • num += step即讀取杜恰,寫入同一塊內(nèi)存获诈,且訪問時(shí)間重疊仍源,造成內(nèi)存訪問沖突心褐,解決方案如下:
var step = 1

func increment(_ num: inout Int) {
    num += step
}

var copyStep = step
increment(&copyStep)
step = copyStep
  • 再看一段代碼:
func balance(_ x: inout Int,_ y: inout Int) {
    let sum = x + y
    x = sum / 2
    y = sum - x
}

var num1 = 42
var num2 = 30

balance(&num1, &num2) //OK
balance(&num1, &num1) //Error
  • balance(&num1, &num1)會(huì)報(bào)錯(cuò),因?yàn)閭鲄⒍际莕um1的內(nèi)存地址笼踩,當(dāng)執(zhí)行y = sum - x就會(huì)出現(xiàn)內(nèi)存訪問沖突逗爹;

  • 那么若傳入一個(gè)結(jié)構(gòu)體的兩個(gè)不同成員,給balance函數(shù)嚎于,肯定會(huì)報(bào)錯(cuò)的掘而,因?yàn)榻Y(jié)構(gòu)體成員都是存儲(chǔ)在結(jié)構(gòu)體中的,是同一塊內(nèi)存于购,所以會(huì)造成內(nèi)存訪問沖突袍睡;

  • 若滿足下面的條件,就說明重疊訪問結(jié)構(gòu)體的成員是安全的:

  • 只訪問實(shí)例存儲(chǔ)屬性肋僧,不是計(jì)算屬性或類屬性斑胜;

  • 結(jié)構(gòu)體是局部變量不是全局變量;

  • 結(jié)構(gòu)體沒有被閉包捕獲 或者 只被非逃逸閉包捕獲嫌吠;

指針

  • 在Swift中有專門的指針類型止潘,這些都被定性為不安全的,常見的有以下四種類型:
  • UnsafePointer<Pointee> 類似于const Pointee *
  • UnsafeMutablePointer<Pointee> 類似于Pointee *
  • UnsafeRawPointer<Pointee> 類似于const void *
  • UnsafeMutableRawPointer<Pointee> 類似于 void *
  • 其中<Pointee>表示泛型辫诅,Mutable表示可修改凭戴;
var age = 10

func test1(_ ptr: UnsafeMutablePointer<Int>)  {
    //修改地址中的值
    ptr.pointee = 20
    print("test1",ptr.pointee)
}

func test2(_ ptr: UnsafePointer<Int>) {
    //訪問地址中的值
    print("test2",ptr.pointee)
}

test1(&age)
test2(&age)
print(age) //20
var age = 10

func test3(_ ptr: UnsafeRawPointer) {
    print("test3",ptr.load(as: Int.self))
}

func test4(_ ptr: UnsafeMutableRawPointer) {
    ptr.storeBytes(of: 30, as: Int.self)
}

test3(&age)//10
test4(&age)
print(age)//30
  • 由于UnsafeRawPointer是任意類型的指針,所以從它內(nèi)存中取數(shù)據(jù)時(shí)炕矮,必須指定數(shù)據(jù)類型么夫,它才知道取多少字節(jié)的數(shù)據(jù),所以必須傳入數(shù)據(jù)類型參數(shù)肤视;

指針的應(yīng)用實(shí)例

var arr = NSArray(objects: 11,22,33,44)
print(arr)

//UnsafeMutablePointer<ObjCBool> stop
arr.enumerateObjects { (element, idx, stop) in
    print(idx,element)
    //idx = 2時(shí)档痪,停止遍歷
    if idx == 2 {
        stop.pointee = true
    }
}
  • stop是UnsafeMutablePointer<ObjCBool> 指針類型;
獲取指向某個(gè)變量的指針
//age的地址值為:0x100008188
var age = 10

//UnsafePointer<Int> 類型
//ptr的值為:0x100008188
var ptr1 = withUnsafePointer(to: &age) { $0 }
print(ptr1.pointee)

var ptr3 = withUnsafePointer(to: &age) { (p) -> UnsafePointer<Int> in
    return p
}


//UnsafeMutableRawBufferPointer
var ptr2 = withUnsafeMutablePointer(to: &age) { $0 }
ptr2.pointee = 20

print(age)

var ptr4 = withUnsafeMutablePointer(to: &age) { UnsafeMutableRawPointer($0) }
ptr4.storeBytes(of: 40, as: Int.self)

var ptr5 = withUnsafePointer(to: &age) { UnsafeRawPointer($0)}
print(ptr5.load(as: Int.self)) //40
  • 獲取變量a的引用指針钢颂;
  • 其中ptr1余ptr2是等價(jià)寫法钞它;
獲取指向堆空間的指針
class Person {
    
}

//person是堆空間的地址值 即實(shí)例對象的地址值
var person: Person = Person()
//personPtr是全局區(qū)的地址值,也就是引用變量的地址值
var personPtr = withUnsafePointer(to: &person) { $0 }
print(personPtr)
print("1111")
Snip20210805_126.png
class Person {
    
}

//person是指針變量 其值是堆空間的地址值
var person: Person = Person()
//personPtr是全局區(qū)的地址值,也就是引用指針變量person的地址值
var personPtr = withUnsafePointer(to: &person) { UnsafeRawPointer($0) }
print(personPtr)
//address是person對象堆空間的地址值
var address = personPtr.load(as: UInt.self)
//ptr2是指向address堆空間的指針變量
var ptr2 = UnsafeMutableRawPointer(bitPattern: address)
print(ptr2)
print("1111")
  • var ptr2 = UnsafeMutableRawPointer(bitPattern: address)遭垛,根據(jù)堆空間獲取一個(gè)指向堆空間的指針變量尼桶;
創(chuàng)建指針
//申請堆空間
var ptr = malloc(16)
//存 前8個(gè)字節(jié)
ptr?.storeBytes(of: 10, as: Int.self)
//存 后8個(gè)字節(jié)
ptr?.storeBytes(of: 20, toByteOffset: 8, as: Int.self)
//取 前8個(gè)字節(jié)
print((ptr?.load(as: Int.self))!) //10
//取 后8個(gè)字節(jié)
print((ptr?.load(fromByteOffset: 8, as: Int.self))!) //20
//銷毀堆內(nèi)存空間
free(ptr)

//另一種寫法 -----------------------------------------------------

//申請堆空間
var ptr = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)
//存 前8個(gè)字節(jié)
ptr.storeBytes(of: 10, as: Int.self)
//存 后8個(gè)字節(jié)
ptr.advanced(by: 8).storeBytes(of: 20, as: Int.self)
//取 前8個(gè)字節(jié)
print(ptr.load(as: Int.self)) //10
//取 后8個(gè)字節(jié)
print(ptr.advanced(by: 8).load(as: Int.self)) //20
//銷毀堆內(nèi)存空間
ptr.deallocate()

//另一種寫法 -----------------------------------------------------

//申請堆空間 8 * 2 = 16個(gè)字節(jié)
var ptr = UnsafeMutablePointer<Int>.allocate(capacity: 2)
//存 前8個(gè)字節(jié)
ptr.initialize(to: 10)
//存 后8個(gè)字節(jié)
ptr.successor().initialize(to: 20)
//取 前8個(gè)字節(jié)
print(ptr.pointee) //10
//取 后8個(gè)字節(jié)
print((ptr+1).pointee) //20
print(ptr[1]) //20
print(ptr.successor().pointee) //20
//銷毀堆內(nèi)存空間
ptr.deinitialize(count: 2)
ptr.deallocate()
class Person {
    var age: Int
    var name: String
    init(age: Int,name: String) {
        self.age = age
        self.name = name
    }
    deinit {
        print("Person deinit")
    }
}

var ptr = UnsafeMutablePointer<Person>.allocate(capacity: 3)
ptr.initialize(to: Person(age: 10, name: "li"))
(ptr+1).initialize(to: Person(age: 20, name: "liyan"))
(ptr+2).initialize(to: Person(age: 30, name: "liyanyan"))

ptr.deinitialize(count: 3)
ptr.deallocate()

指針之間的轉(zhuǎn)換

//任意類型指針
var ptr = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)
//轉(zhuǎn)成int類型指針
var ptr2 = ptr.assumingMemoryBound(to: Int.self)

ptr.assumingMemoryBound(to: Int.self).pointee = 11
(ptr+8).assumingMemoryBound(to: Double.self).pointee = 22.0
//ptr.deallocate()
var ptr = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)
ptr.assumingMemoryBound(to: Int.self).pointee = 11
(ptr+8).assumingMemoryBound(to: Double.self).pointee = 22.0
//var ptr2 = unsafeBitCast(ptr, to: UnsafeMutablePointer<Int>.self)

print(unsafeBitCast(ptr, to:  UnsafeMutablePointer<Int>.self).pointee)//11
print(unsafeBitCast(ptr, to: UnsafeMutablePointer<Double>.self).pointee)//22.0
ptr.deallocate()
  • unsafeBitCast忽略數(shù)據(jù)類型的強(qiáng)制轉(zhuǎn)換,不會(huì)因?yàn)閿?shù)據(jù)類型的變化而改變原來的內(nèi)存數(shù)據(jù)锯仪;
class Person {
    
}

var person = Person()

//personObjectAddress存放的就是person的堆空間地址值
var personObjectAddress = unsafeBitCast(person, to: UnsafeRawPointer.self)
print(personObjectAddress)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末泵督,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子庶喜,更是在濱河造成了極大的恐慌小腊,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件久窟,死亡現(xiàn)場離奇詭異秩冈,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)斥扛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門入问,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人稀颁,你說我怎么就攤上這事芬失。” “怎么了匾灶?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵棱烂,是天一觀的道長。 經(jīng)常有香客問我阶女,道長颊糜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任张肾,我火速辦了婚禮芭析,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘吞瞪。我一直安慰自己馁启,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布芍秆。 她就那樣靜靜地躺著惯疙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪妖啥。 梳的紋絲不亂的頭發(fā)上霉颠,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機(jī)與錄音荆虱,去河邊找鬼蒿偎。 笑死朽们,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的诉位。 我是一名探鬼主播骑脱,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼苍糠!你這毒婦竟也來了叁丧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤岳瞭,失蹤者是張志新(化名)和其女友劉穎拥娄,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瞳筏,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡稚瘾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了乏矾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片孟抗。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖钻心,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情铅协,我是刑警寧澤捷沸,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站狐史,受9級特大地震影響痒给,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜骏全,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一苍柏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧姜贡,春花似錦试吁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至母怜,卻和暖如春余耽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背苹熏。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工碟贾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留币喧,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓袱耽,卻偏偏與公主長得像粱锐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子扛邑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容