swift進(jìn)階八:閉包 & Runtime & Any等類型

swift進(jìn)階 學(xué)習(xí)大綱

  1. 閉包
  2. Runtime
  3. Any、AnyObject 、 AnyClass、self、Type 、type(of)

1. 閉包

閉包,swift中強(qiáng)大的存在,它類似于OC中的Block。

  • 閉包默認(rèn)自動(dòng)捕獲外部變量,可直接對(duì)捕獲變量進(jìn)行操作,影響捕獲變量的值

測(cè)試代碼:

  1. 自動(dòng)捕獲變量,會(huì)影響外部變量
var age = 10
let closure = {
   age += 1
}
closure()
print(age)
  • 打印結(jié)果:
    image.png

    自動(dòng)捕獲了外部變量age,閉包內(nèi)修改age值,影響了外部age
  1. 手動(dòng)捕獲外界變量,值類型對(duì)象,會(huì)被copy一份不可修改,也不影響外部值。
    image.png
  • 如果閉包直接持有外部變量,容易造成循環(huán)引用。
  • 例如:
class HTPerson {
  var age = 18
  var completion: (()->Void)?
  deinit {
      print("HTPerson deinit")
  }
}

func test() {
   var person = HTPerson()
   person.completion = {
      person.age += 1
   }
}

test()

print("end")
  • 打印結(jié)果:
image.png
  • person 持有completion屬性,completion閉包中捕獲person這個(gè)外部變量,造成循環(huán)引用。
  • swift提供兩種方式打破循環(huán):
    【方式一】 weak弱引用
  • weak修飾的對(duì)象,不影響強(qiáng)引用計(jì)數(shù),不影響對(duì)象的釋放。對(duì)象允許nil,nil時(shí)不執(zhí)行后續(xù)語句
image.png

【方式二】unowned無主引用

  • unowned修飾的對(duì)象,不影響強(qiáng)引用計(jì)數(shù),不影響對(duì)象的釋放。假定對(duì)象一直存在,不能nil,必須確保捕獲對(duì)象生命周期可控(當(dāng)對(duì)象已被釋放時(shí),會(huì)存在產(chǎn)生野指針,會(huì)有crash風(fēng)險(xiǎn))
image.png

總結(jié)

  1. swift閉包會(huì)自動(dòng)捕獲外界變量,并影響引用計(jì)數(shù),有循環(huán)引用風(fēng)險(xiǎn)。
  2. 手動(dòng)聲明捕獲變量(寫在[ ]內(nèi)):
    值類型:數(shù)據(jù)會(huì)被copy一份,為只讀屬性,不受該值的后續(xù)變化影響,也會(huì)影響該值。
    引用類型copy的是對(duì)象地址,操作對(duì)象地址,會(huì)影響對(duì)象引用計(jì)數(shù),也會(huì)影響對(duì)象的內(nèi)容。有循環(huán)引用風(fēng)險(xiǎn)。
  3. 打斷循環(huán)引用有兩種方式:
    [weak弱引用]: 將對(duì)象變?yōu)?code>可空類型,不強(qiáng)引用對(duì)象,對(duì)象為nil時(shí),不執(zhí)行后續(xù)語句,很安全。(生命周期跟對(duì)象一致)

[unowned無主引用]:假定對(duì)象一直存在,不強(qiáng)引用對(duì)象,對(duì)象不能nil。必須確保捕獲對(duì)象生命周期可控(當(dāng)對(duì)象已被釋放時(shí),會(huì)存在產(chǎn)生野指針,會(huì)有crash風(fēng)險(xiǎn))

2. Runtime

  • swift是一門靜態(tài)語言,本身不具備動(dòng)態(tài)性,不像OCRuntime運(yùn)行時(shí)的機(jī)制(此處指OC提供運(yùn)行時(shí)API供程序員操作)。但由于swift兼容OC,所以可以轉(zhuǎn)OC類函數(shù),利用OC的運(yùn)行時(shí)機(jī)制,來實(shí)現(xiàn)動(dòng)態(tài)性。

運(yùn)行時(shí)靠邊的內(nèi)容,swift只有dynamic修飾詞Mirror反射機(jī)制。

  • dynamic編譯時(shí)已完成函數(shù)的替換操作,在運(yùn)行前就處理好了。
  • Mirror反射可以動(dòng)態(tài)獲取類型成員信息,在程序運(yùn)行時(shí)調(diào)用方法、屬性行為特性。但沒有OCRuntime那么強(qiáng)大
  • 創(chuàng)建一個(gè)oc項(xiàng)目:
  1. 創(chuàng)建swift類,自動(dòng)生成橋接文件class類必須繼承NSObject,所有需要OC調(diào)用的函數(shù)方法,都必須使用@objc聲明。
class HTPerson: NSObject {
    @objc var age  = 10
    @objc func sayHello() {
        print("sayHello")
    }
}

class HTTool: NSObject {
    
    @objc static func test() {
        
        var methodCount: UInt32 = 0
        let methodList = class_copyMethodList(HTPerson.self, &methodCount)
        for i in 0..<numericCast(methodCount) {
            if let method = methodList?[i] {
                let methodName = method_getName(method)
                print("方法列表:\(String(methodName.description))")
            } else {
                print("not found method!")
            }
        }
       
        var count: UInt32 = 0
        let propertyList = class_copyPropertyList(HTPerson.self, &count)
        for i in 0..<numericCast(count) {
            if let property = propertyList?[i] {
                let propertyName = property_getName(property)
                print("屬性列表:\(String(utf8String: propertyName)!)")
            } else {
                print("not found property!")
            }
        }
    }
}
  1. OC調(diào)用的文件中,必須導(dǎo)入XXX-Swift.h頭文件。確保橋接文件中,已自動(dòng)生成OC類、屬性函數(shù)

    image.png

    image.png

  2. 被調(diào)用時(shí),可以看到我們動(dòng)態(tài)獲取到了@objc聲明屬性函數(shù)

    image.png

  3. 當(dāng)我們調(diào)用swift函數(shù)時(shí),打開匯編模式,可以看到已經(jīng)是使用objc_msgSend進(jìn)行消息發(fā)送了。此時(shí)這些@objc修飾函數(shù)變量,可具備OC運(yùn)行時(shí)特性。

    image.png

    image.png

  • 至此,我們已知道swift借助OC實(shí)現(xiàn)運(yùn)行時(shí)機(jī)制。

進(jìn)一步探究( ?? objc4源碼

  • 我們?cè)?code>objc4源碼中搜索class_copyMethodList(,可以把上述案例代碼復(fù)制在oc源碼中,打斷點(diǎn),可以發(fā)現(xiàn)cls是Swift類。
image.png
  • 如果進(jìn)入data()里面,打印superClass,會(huì)發(fā)現(xiàn)是Swift._SwiftObject

    image.png

  • OC源碼中,有針對(duì)swift一一對(duì)應(yīng)結(jié)構(gòu)關(guān)系

    image.png

  • Swift源碼中,針對(duì)Runtime,有專門遵循NSObject協(xié)議的SwiftObject基類:

    image.png

  • 所以本質(zhì)上swift能實(shí)現(xiàn)OC Runtime的原因,是結(jié)構(gòu)相互兼容。

3. Any、AnyObject 、 AnyClass、self、Type 、type(of)

  • AnyObjetc: 代表任意類的Instance、類的類型(class)、僅類遵守的協(xié)議。(struct不行)
  • Any: 代表任意類型,包括function類型或Optional類型
  • AnyClass: 僅代表實(shí)例的類型( AnyObject.Type)
  • T.self: 如果T是實(shí)例對(duì)象,就返回實(shí)例本身。如果T是,就返回metadata
  • T.Type: 一種類型,T.selfT.Type類型
  • type(of:):用于獲取一個(gè)值的動(dòng)態(tài)類型

我們通過案例,來深刻理解他們的作用與區(qū)別:

3.1 AnyObject

  • 可代表類的Instance實(shí)例、類的類型、類遵守的協(xié)議。 (struct不行)
    在開發(fā)過程中,如果不確定具體對(duì)象類,可使用AnyObject。
class HTPerson {
   var age = 18
}

// 1. 類實(shí)例
var p1: AnyObject =  HTPerson()

// 2. 類的類型
var p2: AnyObject = HTPerson.self

// 3. 類遵守的協(xié)議 (繼承AnyObjec)
protocol JSONMap: AnyObject { }

// 4. struct不是AnyObject類型
// struct報(bào)錯(cuò): [Non-class type 'HTJSON' cannot conform to class protocol 'JSONMap']
// struct HTJSON: JSONMap { }

// 5. 基礎(chǔ)類型強(qiáng)轉(zhuǎn)為Class,就屬于AnyObject
var age: AnyObject = 10 as NSNumber  // Int不屬于AnyObject,強(qiáng)轉(zhuǎn)NSNumber就屬于AnyObject

3.2 Any

  • AnyAnyObject代表的范圍更廣,不僅支持類實(shí)例對(duì)象、類類型、類協(xié)議。還支持struct函數(shù)以及Optioanl可選類型。
class HTPerson {
   var age = 18
}

// 1. 類實(shí)例
var p1: Any =  HTPerson()

// 2. 類的類型
var p2: Any = HTPerson.self

// 3. 類遵守的協(xié)議 (繼承AnyObjec)
protocol JSONMap: Any { }

// 4. struct
struct HTJSON: JSONMap { }

// 5. 函數(shù)
func test() {}

// 6. struct對(duì)象
let s = HTJSON()

// 7. 可選類型
let option: HTPerson? = nil

// Any類型的數(shù)組
var array: [Any] = [1,                // Int
                   "2",              // String
                   HTPerson.self,    // class類型
                   p1,               // 類的實(shí)例對(duì)象
                   JSONMap.self,     // 協(xié)議本身
                   HTJSON.self,      // struct類型
                   s,                // struct實(shí)例對(duì)象
                   option,           // 可選值
                   test()            // 函數(shù)
                   ]
print(array) 
// 打印結(jié)果: [1, "2", Demo.HTPerson, Demo.HTPerson, Demo.JSONMap, Demo.HTJSON, >Demo.HTJSON(), nil, ()]
  • 通過[Any]數(shù)組,我們可以看到Any指代范圍有多

3.3 AnyClass

  • AnyClass僅代表類型。
    雖可接受AnyObject所有對(duì)象,但實(shí)際只存儲(chǔ)類型(通過下面[AnyObject]數(shù)組可以觀察到這一現(xiàn)象)
class HTPerson {
    var age = 18
}

// 1. 類實(shí)例
var p1: AnyObject =  HTPerson()

// 2. 類的類型
var p2: AnyObject = HTPerson.self

// 3. 類遵守的協(xié)議 (繼承AnyObjec)
protocol JSONMap: AnyObject { }

// 4. struct 不支持
//struct HTJSON: JSONMap { }

class HTTest: JSONMap { }
var p3: JSONMap = HTTest()

// 7. 可選類型
let option: HTPerson? = nil

// Any類型的數(shù)組
var array: [AnyObject] = [ HTPerson.self,    // class類型
                           p1,               // 類的實(shí)例對(duì)象
                           p3                // 遵守AnyObject協(xié)議的類對(duì)象也符合(類對(duì)象本身符合)
                         ]
print(array) // 打印結(jié)果:  [Demo.HTPerson, Demo.HTPerson, Demo.HTTest]

3.4 T.self

  • 如果T實(shí)例對(duì)象,就返回實(shí)例本身。如果T,就返回metadata(首地址:類的類型
class HTPerson {
    var age = 18
}

struct HTTest {
    var name = "ht"
}

// 1. class實(shí)例對(duì)象,返回對(duì)象本身
var p =  HTPerson().self
print(type(of: p))        // 打印: HTPerson(實(shí)例對(duì)象類型)

// 2. class類型, 返回class類型
var pClass = HTPerson.self
print(type(of: pClass))   // 打印: HTPerson.Type(class類型)

// 3. struct實(shí)例對(duì)象,返回對(duì)象本身
var t = HTTest().self
print(type(of: t))       // 打?。?HTTest (實(shí)例對(duì)象類型)

// 4. struct類型,返回struct類型
var tStruct = HTTest.self
print(type(of: tStruct))  // 打?。?HTTest.Type(struct類型)

3.5 T.Type

  • 一種類型,T.selfT.Type類型。(使用type(of:)讀取)
  • class類型的Type


    image.png
  • struct類型的Type


    image.png

3.6 type(of:)

  • 用于獲取一個(gè)值的動(dòng)態(tài)類型
var age = 10

// 編譯器任務(wù)value接收Any類型
func test(_ value: Any) {
   
   // type(of:)可獲取真實(shí)類型
   print(type(of: value))    // 打印Int
   
}

test(age)
  • 編譯期valueAny類型,運(yùn)行時(shí)type(of:)可獲取真實(shí)類型

    image.png

    1. 父子類的調(diào)用,type(of:)是讀取真實(shí)調(diào)用對(duì)象
class HTPerson { }

class HTStudent: HTPerson { }

func test(_ value: HTPerson) {
   print(type(of: value)) // 當(dāng)前真實(shí)調(diào)用對(duì)象
}

var person = HTPerson()
var student = HTStudent()

test(person)  // 打印:HTPerson
test(student)  // 打?。篐TStudent
    1. 遵循協(xié)議的類調(diào)用,,type(of:)也是讀取真實(shí)調(diào)用對(duì)象,而不是協(xié)議:
protocol TestProtocol { }

class HTPerson: TestProtocol { }

func test(_ value: TestProtocol) {
   print(type(of: value)) // 當(dāng)前真實(shí)調(diào)用對(duì)象(不是協(xié)議)
}

var p = HTPerson()
var p1: TestProtocol = HTPerson()

test(p)   // 打?。?HTPerson
test(p1)  // 打?。?HTPerson  (注意,不是協(xié)議)

補(bǔ)充: 當(dāng)使用泛型T時(shí),調(diào)用時(shí),可確定T類型。type(of:)讀取的就是T類型

protocol TestProtocol { }

class HTPerson: TestProtocol { }

func test<T>(_ value: T) {
   print(type(of: value)) // (是協(xié)議)   如果需要取到類,可將value強(qiáng)轉(zhuǎn)為Any類型 (value as Any)
}

var p = HTPerson()
var p1: TestProtocol = HTPerson()

test(p)   // 打?。?HTPerson
test(p1)  // 打印: TestProtocol  (是協(xié)議)
  • 以上,就是swift的閉包RuntimeAny & AnyObject & AnyClass & Type類型的詳細(xì)介紹。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,431評(píng)論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,637評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,555評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,900評(píng)論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,629評(píng)論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,976評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,976評(píng)論 3 448
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,139評(píng)論 0 290
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,686評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,411評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,641評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,129評(píng)論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,820評(píng)論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,233評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,567評(píng)論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,362評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,604評(píng)論 2 380

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