Runtime-b
感謝大神分享
依舊是網上很多runtime的資料,依舊是看不懂,,,這里給大家轉化一下runtime,使它由隱晦難懂變得通俗易懂。
(雖然截圖和語言組織的有些凌亂,但是大家還是一點一點的閱讀下去吧,可以新建一個工程,跟著我寫的一步一步的自己走一遍,會有幫助的。)
關于runtime理論性的東西可以參考我們同事hah的初識runtime(我倆的文章名字居然差不多)
本文參照:教你快速上手Runtime。謝謝該文作者崢吖崢老師。有興趣的也可以去崢老師的博客看看去。
另外哪里寫的不對,可以給我留言,我會及時改進的,謝謝大家。
在這里我們對runtime進行如下的介紹:
1、runtime的簡單介紹
2、runtime的消息發送
3、用runtime交換方法
4、動態添加方法
5、動態添加屬性
6、runtime替換KVC,進行字典轉換
?
下邊開始我們的通俗易懂的路程:
1、配置環境:新建一個工程,在appdelegate里面將viewcontroller設置為rootcontroller,方便查看打印信息。
創建一個person類,繼承自NSObject,下圖的PersonViewController無用。
然后再看一下目前的工程文件結構:
按照網上教程我們開始runtime的各個方法的實現。
2、發送消息。舉例:實例方法和類方法的調用原理。
我們建立一個繼承于NSObject的Person
在.m中分別實現實例方法和類方法:
然后在viewcontroller加入頭文件:#import "Person.h"、#import <objc/message.h>
方法的調用有兩種,調用實例方法和調用類方法。我們用runtime去觀察這兩種方法的實質。
1、viewDidLoad里面實現Person的實例化,并用這個實例調用Person里面的實例方法;
2、直接用Preson類名或類對象調用類方法。
此時我們可以看到
但是按照網上教程上圖藍色框框里面的也應該可以放開注釋的,可是不知道為什么我的放開注釋以后會爆紅:
不知道是為什么(如果大家誰知道怎么回事,還煩請留言告訴我,再次謝謝大家了),,,所以這里的runtime并不友好。
哎,終于知道怎么回事了,還是同事hah幫忙解決的:頭文件導入錯了,應該是導入:#import <objc/runtime.h>
導入#import<objc/messge.h>也可以調用objc_msgSend()這個方法,但是不能傳參,而導入#import<objc/runtime.h>,會出現這個方法:objc_msgSend(id, SEL, ...),是可以傳參數的.
在這里有的網友提到另外一種解決該問題的方案:謝謝北京-吳露。方案是:引入頭文件<objc/objc-runtime>,command+點擊進入這個方法,我們會看到下邊的
也就是說這個頭文件把runtime和message都包含了,不過建議還是單獨引入,因為這兩個頭文件里面的方法很多都類似,容易混淆,區別在于方法是否可以傳參數。就像上邊我們舉例的問題。
打印結果:
其實這里咱們可以看到網上教程的注釋:不管是實例方法還是類方法,其本質就是讓對象或者類對象發送消息,即我們在OC中調用的實例方法和類方法在runtime運行時的時候實際上是轉化成C語言的發送消息的語句。
按照網上教程說的:消息機制原理:對象根據方法編號SEL去映射表查找對應的方法實現
其實說的就是:找到對應的實例方法或者類方法。
最后,這里的runtime的使用并不實用。
3、交換方法。
其實寫完下邊的代碼,在通過理解,你會發現這中所謂的“交換方法”實際上就是系統方法的重寫、擴展、再替換回去的過程。
首先我們看一下demo里面viewcontroller的結構(同樣的personviewcontroller無用,忽略掉,這里的<objc/message.h>最好是換成#import<objc/runtime.h>):
然后我們看viewDidLoad里面,在這里我們沒有做什么操作,只是命名了一個image:這里我們的圖片是在工程文件中的,所以走到替換方法里面的時候會判斷不為空,所以對應的打印信息“加載空的圖片”就不會打印出來。
按照網上教程我們進行步驟一的操作,寫一個名字叫做+ (instancetype)imageWithName:(NSString*)name的類方法。這個方法是為了替換的時候準備的。
然后我們進行步驟二,這個步驟二很關鍵,就是替換的核心所在。
注意:兩個方法。前面一個是準備好的方法,后邊一個是系統的方法,要用第一個方法換掉第二個方法,這里需要注意替換順序。
當我們將viewDidLoad里面的image換成一個空的圖片的時候
我們看到打印信息
是走了圖片為空的if判斷里面的。
所以,這個方法可以看成是網絡請求一張圖片,如果請求失敗,可以用默認的圖片進行展示,就像下邊的方法,網絡請求失敗的時候會展示一張默認圖。
當然,這里不是說runtime這么高大上的方法僅僅能實現這么low的功能,僅做示例而已。
其實這個方法替換可以用在線下替換線上代碼里面,“黑魔法”,我還不會,只是聽說過。下邊繼續學習runtime其他的方法。
4、動態添加方法。
動態添加方法,咱們的person類.h里面用到了分類:
在這里拓展一下類擴展的知識:類擴展與分類的區別。謝謝作者:Mitchell孟晨
在這里大家是不是會想到我們的面試題中:
有的還是用英語問的:
類的作用。參考自category解析。如果大家對類別特別感興趣可以參考深入解析OC中的Category
當我們在類中添加@interface的時候:
會出現上圖中的3個。
第一個就是類:繼承自誰。
第二個是分類,就是分類。
類擴展與分類的區別中有舉例說明:雖然我們在分類中聲明屬性不會報錯,但是@property并沒有自動為我們設置的屬性生成set、get方法。
第三個就是extension。類擴展。
再看上述中有一句話是這樣說的:
類擴展的作用:
person類.m中:
viewcontroller里面:
這里沒有實現eat4方法,所以會在viewcontroller中的viewdidload里面調用eat4的時候,會調用這個方法處理,并且把對應的方法列表傳過來。用來判斷為實現的方法是不是我們想要動態添加的方法。
這里注意的是,崢老師教程里面在調用class_addMethod(self,@selector(eat4), (IMP)eatt,"v@:");這個方法的時候,第三個參數前面并沒有添加“(IMP)”,不添加這個IMP,會報黃:
說的什么不清楚,意思就是叫你添加上IMP。
拓展一下performSelector調用和直接調用的區別:
準備好了,我們看打印結果:
這里我們可以看到NSStringFromSelector(sel)調用的就是eat4。
扯了這么多怎么用,到底這個動態添加方法到底是用來干什么的呢,看看眾多網友的理解。翻了好多,就下邊的兩個解釋比較靠譜,大家湊合著看吧:
當然還有最一開始咱們引入動態添加的時候的目的:
不忘初心,方得始終。
5、動態添加屬性。
想知道原理的請移步OC Associated Objects實現原理
原理:給一個類聲明屬性,其實本質就是給這個類添加關聯,并不是直接把這個值的內存空間添加到類存空間。
看結構,添加了一個NSObject的分類:
分類的.h文件,將set方法和name外漏,方便在viewcontroller里面調用。
分類的.m文件,set方法和name的實現。
最后看viewcontroller里面,引入分類的頭文件,然后實例化NSObject,調用objc1的name。
最后的打印結果:
tips:其實呢這個動態添加屬性,是在哪里用到的呢?具體的我也說不上來。其實按照我的理解,咱們這里所謂的動態添加屬性,是在分類上邊添加的屬性,那么我就想了,為什么我不能在類自己本身上邊添加屬性呢?對吧?為什么非要在分類上邊添加呢,還整了個動態添加屬性,麻煩不?!原因有兩個:
1)如果你的類自己本身里面的邏輯或者代碼比較多,而需求是新加一個功能,那么就可以在分類上邊添加,因為分類和繼承還是有區別的。咱們這樣理解啊:如果你用的是繼承,那么在子類a里面新添加的屬性和方法,在父類以及其他繼承父類的子類b里面是不能用新添加的屬性和方法的,(有點繞)。但是分類就行,你在分類里面新添加了屬性和方法,那么在其他用到這個類自己的地方,是可以用新添加的屬性和方法的。好像和公有性、私有性有點關系。
2)給系統的類添加屬性。像UIView、UIImageView、UILabel、UIButton等等系統的類,你覺得系統的類里面的屬性不夠用(當然,像我這種小白還是覺得夠用的,關鍵不知道各個類里面除了經常用的還有啥,,,)就可以自己添加屬性,當然了,在MJ和SDWebimage里面其實也能看到動態添加屬性的影子,只不過是我們大多數人沒有進入人家里面去看具體的代碼。同事hah在runtime初識里面也提到了這個:
6、字典轉模型
崢老師說的“字典轉模型的方式一:KVC”這個就是咱們平時JSON解析的時候經常用到的,這里不作進一步解釋,看看崢老師說的用runtime字典轉模型的方式。
沒搞明白。。。
先用KVC吧。
?
?
?
總的來說,即便咱們知道了runtime是怎么用的,但是沒有合適的環境使用runtime,也是白搭。所以,咱們現在能做的就是先了解runtime,隨時記在心中,以后哪里能夠用到的,可以嘗試著用runtime的特性和上述幾個方法來實現,甚至于用runtime解決bug。
另外這里有很多關于runtime的集合,大家可以參考:runtime專題
原文鏈接:http://www.jianshu.com/p/4fd0ef1a144b
著作權歸作者所有,轉載請聯系作者獲得授權,并標注“簡書作者”。
總結
- 上一篇: RecyclerView添加header
- 下一篇: Android第二十八天