swift Runtime

Runtime簡(jiǎn)稱運(yùn)行時(shí)衩茸。OC就是運(yùn)行時(shí)機(jī)制,也就是在程序運(yùn)行時(shí)候的一些機(jī)制筏勒,其中最主要的是消息機(jī)制。對(duì)于我們熟悉的C語(yǔ)言旺嬉,函數(shù)的調(diào)用在編譯的時(shí)候會(huì)決定調(diào)用哪個(gè)函數(shù)管行。但對(duì)于OC的函數(shù),屬于動(dòng)態(tài)調(diào)用過(guò)程鹰服,在編譯的時(shí)候并不能決定真正調(diào)用哪個(gè)函數(shù)病瞳,只有在真正運(yùn)行的時(shí)候才會(huì)根據(jù)函數(shù)的名稱找到對(duì)應(yīng)的函數(shù)來(lái)調(diào)用。

 也就有了下面這兩點(diǎn)結(jié)論:
 1悲酷、在編譯階段套菜,OC可以調(diào)用任何函數(shù),即使這個(gè)函數(shù)并未實(shí)現(xiàn)设易,只要聲明過(guò)就不會(huì)報(bào)錯(cuò)逗柴。
 2、在編譯階段顿肺,C語(yǔ)言調(diào)用未實(shí)現(xiàn)的函數(shù)就會(huì)報(bào)錯(cuò)戏溺。

我們定義一個(gè)純Swift的類 TestASwiftClass ,代碼如下:

class TestASwiftClass{  
var aBoll :Bool = true
var aInt : Int = 0
func testReturnVoidWithaId(aId : UIView) {
    print("TestASwiftClass.testReturnVoidWithaId")
 }

再寫一個(gè)繼承自 UIViewController 的 ViewController

class ViewController: UIViewController{
let testStringOne  = "testStringOne"
let testStringTwo  = "testStringTwo"
let testStringThr  = "testStringThr"
var count:UInt32 = 0
override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    let  SwiftClass = TestASwiftClass()
    let  proList = class_copyPropertyList(object_getClass(SwiftClass),&count)
    for  i in 0..<numericCast(count) {
            let property = property_getName(proList?[i]);
            print("屬性成員屬性:%@",String.init(utf8String: property!) ?? "沒(méi)有找到你要的屬性");
    }
}
override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

}
上面的代碼也很簡(jiǎn)單屠尊,我們?cè)赩iewController中添加了一些變量旷祸,然后通過(guò)Runtime的方法嘗試著先來(lái)獲取一下我們最上面定義的純Swift類TestASwiftClass的屬性,你運(yùn)行上面代碼你就會(huì)發(fā)現(xiàn):

什么都沒(méi)有K侠ァM邢怼!為什么浸赫?闰围?

下面我們先給出答案,用它來(lái)解釋一下為什么我們通過(guò)上面Runtime的API沒(méi)有獲取到任何東西既峡,然后再接著用OC來(lái)證明一下我們說(shuō)的結(jié)論:

C 語(yǔ)言是在函數(shù)編譯的時(shí)候決定調(diào)用那個(gè)函數(shù)羡榴,在編譯階段,C要是調(diào)用了沒(méi)有實(shí)現(xiàn)的函數(shù)就會(huì)報(bào)錯(cuò)运敢。
OC 的函數(shù)是屬于動(dòng)態(tài)調(diào)用校仑,在編譯的時(shí)候是不能決定真正去調(diào)用那個(gè)函數(shù)的,只有在運(yùn)行的時(shí)候才能決定去調(diào)用哪一個(gè)函數(shù) 传惠,在編譯階段迄沫,OC可以調(diào)用任何的函數(shù),即使這個(gè)函數(shù)沒(méi)有實(shí)現(xiàn)涉枫,只要聲明過(guò)也就不會(huì)報(bào)錯(cuò)邢滑。
純Swift類的函數(shù)的調(diào)用已經(jīng)不是OC的運(yùn)行時(shí)發(fā)送消息,和C類似,在編譯階段就確定了調(diào)用哪一個(gè)函數(shù)困后,所以純Swift的類我們是沒(méi)辦法通過(guò)運(yùn)行時(shí)去獲取到它的屬性和方法的乐纸。
Swift 對(duì)于繼承自O(shè)C的類,為了兼容OC摇予,凡是繼承與OC的都是保留了它的特性的汽绢,所以可以使用Runtime獲取到它的屬性和方法等等其他我們?cè)贠C中獲得的東西。
針對(duì)上面給出的結(jié)論侧戴,我們看看Swift對(duì)于繼承自O(shè)C的類是不是保留了OC所有的特性呢宁昭?再看下面代碼,只是做一個(gè)簡(jiǎn)單的修改,把通過(guò)object_getClass方法獲取的對(duì)象寫成self:
let  proList = class_copyPropertyList(object_getClass(self),&count)
for  i in 0..<numericCast(count) {
   let property = property_getName(proList?[i]);
   print("屬性成員屬性:%@",String.init(utf8String: property!) ?? "沒(méi)有找到你要的屬性");
}

可以看到我們獲取到了我們?cè)赩iewController中定義的變量酗宋。這樣也就證明了的確是上面答案說(shuō)的那樣积仗。

那這樣就又衍生出一個(gè)問(wèn)題
那Swiftw就沒(méi)辦法利用Runtime了嗎?

想一想蜕猫,要是真的Swift沒(méi)辦法利用Runtime寂曹,那是一件得多讓人失望的事!答案也肯定是否定的回右,我們還是能讓Swift用Runtime的隆圆。看下面的代碼:

class TestASwiftClass{
    dynamic  var aBoll :Bool = true
    var aInt : Int = 0
    dynamic func testReturnVoidWithaId(aId : UIView) {
print("TestASwiftClass.testReturnVoidWithaId")
    }
}

嗯翔烁,我們利用了dynamic(英文單詞動(dòng)態(tài)的意思)關(guān)鍵字渺氧,在第一個(gè)變量和方法的定義前面我們添加了這個(gè)關(guān)鍵字,那添加了這個(gè)關(guān)鍵字之后又什么變化呢蹬屹?我們?cè)偻ㄟ^(guò)最開(kāi)始我們獲取純Swift類的代碼獲取一下試試侣背,看結(jié)果!

結(jié)果:

可以看到這里是獲取到了變量了的哩治。(這里是獲取屬性沒(méi)有寫獲取方法代碼所以是值拿到變量沒(méi)有拿到方法)

aBoll 這個(gè)變量前面是添加了dynamic關(guān)鍵字的秃踩,我們獲取到了衬鱼。在aInt這個(gè)變量前面我們是沒(méi)有添加的业筏,所以可以看到我們是沒(méi)有獲取到這個(gè)變量的,那關(guān)鍵的就是我們要理解:dynamic 關(guān)鍵字的含義:

首先有 @objc 這個(gè)關(guān)鍵字鸟赫,它是用來(lái)將Swift的API導(dǎo)出來(lái)給 Object-C 和 Runtime 使用的蒜胖,如果你類繼承自O(shè)C的類,這個(gè)標(biāo)識(shí)符就會(huì)被自動(dòng)加進(jìn)去抛蚤,加了這標(biāo)識(shí)符的屬性台谢、方法無(wú)法保證都會(huì)被運(yùn)行時(shí)調(diào)用,因?yàn)镾wift會(huì)做靜態(tài)優(yōu)化岁经,想要完全被聲明成動(dòng)態(tài)調(diào)用朋沮,必須使用 dynamic 標(biāo)識(shí)符修飾,當(dāng)然添加了 dynamic 的時(shí)候缀壤,它會(huì)自己在加上@objc這個(gè)標(biāo)識(shí)符樊拓。

這樣我們就理解了dynamic這個(gè)關(guān)鍵字纠亚,知道了它的作用,那我們接下來(lái)就是嘗試著多使用一下 Swift Runtime筋夏。

Swift Runtime

1蒂胞、獲取方法:

    let methodList = class_copyMethodList(object_getClass(SwiftClass), &count)
    for ind in 0..<numericCast(count) {
        let method = method_getName(methodList![ind])
        print("屬性成員方法:",String.init(NSStringFromSelector(method)))
    }

2、屬性成員變量:

    let IvarList = class_copyIvarList(object_getClass(SwiftClass),&count)
    for index in 0..<numericCast(count) {
        
        let Ivar = ivar_getName((IvarList?[index])!)
        print("屬性成員變量:",String.init(utf8String: Ivar!) ?? "沒(méi)有找到你想要的成員變量")
    }

3条篷、協(xié)議列表:

    let protocalList = class_copyProtocolList(object_getClass(self),&count)
    for index in 0..<numericCast(count) {
        
        let protocal = protocol_getName((protocalList?[index])!)
        print("協(xié)議:",String.init(utf8String: protocal) ?? "沒(méi)有找到你想要的協(xié)議")
    }

4骗随、方法交換

這個(gè)就是Runtime的一個(gè)重點(diǎn)了,仔細(xì)說(shuō)一說(shuō)赴叹。
OC的動(dòng)態(tài)性最常用的其實(shí)就是方法的替換鸿染,將某個(gè)類的方法替換成自己定義的類,從而達(dá)到Hook的作用乞巧。(以前面試有人問(wèn)過(guò)OC怎樣Hook一個(gè)消息牡昆,那時(shí)候太懵懂,不知道怎么說(shuō)摊欠!不知道大家有沒(méi)有遇到過(guò)丢烘?)
對(duì)于純粹的Swift類,由于前面的測(cè)試你知道無(wú)法拿到類的屬性方法等些椒,也就沒(méi)辦法進(jìn)行方法的替換播瞳,但是對(duì)于繼承自NSObject的類,由于集成了OC的所有特性免糕,所以是可以利用Runtime的屬性來(lái)進(jìn)行方法替換赢乓,記得我們前面說(shuō)的dynamic關(guān)鍵字。

func ChangeMethod() -> Void {
     
    // 獲取交換之前的方法
    let originaMethodC = class_getInstanceMethod(object_getClass(self), #selector(self.originaMethod))
    // 獲取交換之后的方法
    let swizzeMethodC  = class_getInstanceMethod(object_getClass(self), #selector(self.swizzeMethod))

     //替換類中已有方法的實(shí)現(xiàn),如果該方法不存在添加該方法
     //獲取方法的Type字符串(包含參數(shù)類型和返回值類型)
     //class_replaceMethod(object_getClass(self), #selector(self.swizzeMethod), method_getImplementation(originaMethodC), method_getTypeEncoding(originaMethodC))
     
     print("你交換兩個(gè)方法的實(shí)現(xiàn)")
     method_exchangeImplementations(originaMethodC, swizzeMethodC)
}

dynamic func originaMethod() -> Void {
             
     print("我是交換之前的方法")
}
    
dynamic func swizzeMethod() -> Void {
             
     print("我是交換之后的方法")
}

5石窑、關(guān)聯(lián)屬性

說(shuō)上面的方法Hook比較重要的話牌芋,這個(gè)關(guān)聯(lián)屬性也是比較重要的,在前面我總結(jié)OC的Runtime的時(shí)候在方法的添加這里專門有提過(guò)一個(gè)Demo松逊,我們把這個(gè)Demo重新整理一下躺屁,導(dǎo)航的漸變就是利用Runtime給導(dǎo)航添加屬性來(lái)實(shí)現(xiàn)的。

extension UINavigationBar {
     
var navigationGradualChangeBackgroundView:UIView?{
 
    get{
        return objc_getAssociatedObject(self, &self.navigationGradualChangeBackgroundView) as? UIView;
    }
    set{
     
        objc_setAssociatedObject(self, &self.navigationGradualChangeBackgroundView, navigationGradualChangeBackgroundView, objc_AssociationPolicy.OBJC_ASSOCIATION_COPY_NONATOMIC)
    }
}
 
func setNavigationBackgroundColor (backgroundColor: UIColor) -> Void {
     
    if (self.navigationGradualChangeBackgroundView == nil) {
         
        self.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
        self.navigationGradualChangeBackgroundView = UIView.init(frame: CGRect.init(x: 0, y: -20, width: SCREENWIDTH, height: self.bounds.size.height + 20))
        self.navigationGradualChangeBackgroundView!.isUserInteractionEnabled = false
        self.insertSubview(self.navigationGradualChangeBackgroundView!, at: 0)
    }
 
   self.navigationGradualChangeBackgroundView!.backgroundColor = backgroundColor
}
 
func removeNavigationBackgroundColor() -> Void {
     
   self.setBackgroundImage(nil, for: UIBarMetrics.default)
   self.navigationGradualChangeBackgroundView!.removeFromSuperview()
   self.navigationGradualChangeBackgroundView = nil
}
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末经宏,一起剝皮案震驚了整個(gè)濱河市犀暑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌烁兰,老刑警劉巖耐亏,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異沪斟,居然都是意外死亡广辰,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)择吊,“玉大人袱耽,你說(shuō)我怎么就攤上這事「煞ⅲ” “怎么了朱巨?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)枉长。 經(jīng)常有香客問(wèn)我冀续,道長(zhǎng),這世上最難降的妖魔是什么必峰? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任洪唐,我火速辦了婚禮,結(jié)果婚禮上吼蚁,老公的妹妹穿的比我還像新娘凭需。我一直安慰自己,他們只是感情好肝匆,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布粒蜈。 她就那樣靜靜地躺著,像睡著了一般旗国。 火紅的嫁衣襯著肌膚如雪枯怖。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,760評(píng)論 1 289
  • 那天能曾,我揣著相機(jī)與錄音度硝,去河邊找鬼。 笑死寿冕,一個(gè)胖子當(dāng)著我的面吹牛蕊程,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播驼唱,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼藻茂,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了曙蒸?” 一聲冷哼從身側(cè)響起捌治,我...
    開(kāi)封第一講書(shū)人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤岗钩,失蹤者是張志新(化名)和其女友劉穎纽窟,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體兼吓,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡臂港,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片审孽。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡县袱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出佑力,到底是詐尸還是另有隱情式散,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布打颤,位于F島的核電站暴拄,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏编饺。R本人自食惡果不足惜乖篷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望透且。 院中可真熱鬧撕蔼,春花似錦、人聲如沸秽誊。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)锅论。三九已至诉探,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間棍厌,已是汗流浹背肾胯。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留耘纱,地道東北人敬肚。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像束析,于是被迫代替她去往敵國(guó)和親艳馒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348