iOS 利用Runtime(運行時) 動態(tài)添加屬性、方法、交換方法、發(fā)送消息

第一部分:【很重要,這個必須先看】

runtime.h文件中有如下方法,該方法實現(xiàn)了動態(tài)添加屬性的功能,這里說明一下含義。

OBJC_EXPORT void objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,id _Nullable value, objc_AssociationPolicy policy);


解釋:

參數(shù)一:id _Nonnull object:給哪個對象添加關(guān)聯(lián)。self的話,就是給當前類添加關(guān)聯(lián)
參數(shù)二:const void * _Nonnull key:setter和getter方法中這個參數(shù)一定要保持一致,就是圖二中的userKey,【詳見圖一】。相當于字典中的key。
參數(shù)三:id _Nullable value:外界傳遞過來的value,就是圖一中的userID。相當于字典中的value。
參數(shù)四:objc_AssociationPolicy policy:關(guān)聯(lián)的策略【關(guān)聯(lián)策略的枚舉值下面有界面】。

圖一:
image.png

runtime.h文件中關(guān)聯(lián)的策略有以下枚舉值,這里說明一下含義。
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,           /**< Specifies a weak reference to the associated object. */
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object. 
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /**< Specifies that the associated object is copied. 
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_RETAIN = 01401,       /**< Specifies a strong reference to the associated object.
                                            *   The association is made atomically. */
    OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied.
                                            *   The association is made atomically. */
};

解釋:

OBJC_ASSOCIATION_ASSIGN 等價 @property(assign)
OBJC_ASSOCIATION_RETAIN_NONATOMIC 等價 @property( nonatomic, strong)
OBJC_ASSOCIATION_COPY_NONATOMIC: @property( nonatomic,copy)
OBJC_ASSOCIATION_RETAIN 等價 @property(atomic,strong)
OBJC_ASSOCIATION_COPY 等價 @property(atomic,copy)


第二部分:

方法調(diào)用本質(zhì)

  • 利用runtime發(fā)送消息(即讓對象發(fā)送消息,這個對象指的是創(chuàng)建出類的對象和類對象,是兩個)

    • 若想使用runtime消息,必須導入#import <objc/message.h>框架或者#import <objc/runtime.h>
    • 若想知道發(fā)送消息的代碼格式:
      • 先在終端中cd到運行程序的目錄
      • 再輸入 clang -rewrite-objc main.m 查看最終生成代碼
  • 消息機制的使用場景:

    • 調(diào)用私有方法
    • 調(diào)用系統(tǒng)底層的(沒有暴露出來的)方法
    • Runtime是在不得不使用的時候采用的。

第三部分:

(Runtime)消息機制

  • 對象調(diào)用對象方法||類對象調(diào)用類方法。
    • 當使用后者時,類是一個類對象,所以用[類名 class]獲取類對象,再用類對象調(diào)用類方法.
    • 區(qū)分OC語言哦,OC語言直接用類名調(diào)用類方法即可。不需要向上面那么用創(chuàng)建出來的類對象去調(diào)用
33-01.png

第四部分:

利用Runtime動態(tài)添加方法

  • 為什么動態(tài)添加方法?
    • 因為有些方法可能會就不會用到,所以O(shè)C都是懶加載機制。例如:會員機制,只有是會員,才具有某些功能(才會在代碼的懶加載方法中讓你成為會員),你不是會員,你永遠不會被執(zhí)行懶加載方法,不會讓你在懶加載中實現(xiàn)成為會員的功能
33-02.png

第五部分:

普通添加屬性

  • 僅僅通過分類給NSObject中添加name屬性
34-03.png

利用Runtime動態(tài)添加屬性

  • 給分類只能擴充方法,一般情況不能擴充屬性。
  • 如果想要擴充屬性,必須用到RunTime添加屬性,也就是動態(tài)添加屬性。
  • 普通添加屬性的那個截圖不屬于擴充屬性的范疇,_name不是.h屬性底層的_name,而是定義的靜態(tài)全局變量。(后面的可不看)因為在分類中添加屬性,不會生成方法的實現(xiàn)和下劃線開頭的變量_name,你只能通過重寫setter和getter方法的方式完成,但是_name不會生成,所以在setter方法中,等號左邊的_name不存在,getter方法中的_name我們也不存在。通過定義一個Static修飾的下劃線開頭的這個全局變量_name,可以解決問題,但是這個_name是我們自己定義的,不是系統(tǒng)自動成的。
  • NSObjct這個基類本身沒有name屬性,可以通過分類+Runtime相結(jié)合的方式動態(tài)添加屬性
34-15.png
34-02.png

第六部分:

利用Runtime交換方法

  • 有的時候,系統(tǒng)的類不能滿足要求時,例如系統(tǒng)類(NSString,UIImage)可能并不能滿足我們的要求,解決辦法:
    • 1.往往是給系統(tǒng)自帶的類添加分類,就是對原有的類進行擴充方法,但是切記擴充的方法不要和系統(tǒng)的類相同.
    • 2.或者自定義一個類繼承系統(tǒng)的類,再重寫父類底層的方法??梢赃_到給系統(tǒng)的類自定義某個功能的目的。
    • 3.如果老板要求外界調(diào)用的類方法必須是系統(tǒng)的類方法,即給imageNamed方法提供功能,每次加載圖片就判斷下圖片是否加載成功。那么上述兩種情況就滿足不了條件,可以使用Runtime運行時機制,即 調(diào)用imageNamed:的方法,實際上調(diào)用了ZBimageNamed:方法
      • 3.1:創(chuàng)建分類
      • 3.2:寫一個這樣功能的方法
      • 3.3:用系統(tǒng)的方法與有這個功能的方法交換
      • 3.4:調(diào)用imageNamed,先會調(diào)用分類的load方法,在load方法實現(xiàn)交換,然后才會去調(diào)用分類的ZBimageNamed
      • 具體步驟:在分類中調(diào)用load方法,導入runtime框架,load方法中寫上獲取兩個交換的類的類名,然后寫上method_exchangexxxxx,實現(xiàn)交換。外界調(diào)用imageNamed:的方法,實際上調(diào)用了ZBimageNamed。代碼如下:

ViewController.h文件
 // 需求:給imageNamed方法提供功能,每次加載圖片就判斷下圖片是否加載成功。
    // 步驟一:先搞個分類,定義一個能加載圖片并且能打印的方法+ (instancetype)ZBimageNamed:(NSString *)name;
    // 步驟二:交換imageNamed和ZBimageNamed的實現(xiàn),就能調(diào)用ZBimageNamed,間接調(diào)用ZBimageNamed的實現(xiàn)。
    
    //表面上調(diào)用imageNamed,實際上跑到了分類中調(diào)用了ZBimageNamed,
    //在ZBimageNamed方法中又調(diào)用了ZBimageNamed,實際上調(diào)用了系統(tǒng)底層的imageNamed
    //注意:分類中的ZBimageNamed方法中的ZBimageNamed不能換成imageNamed,否則真實會調(diào)用ZBimageNamed,從而造成死循環(huán)
// 既能加載圖片又能打印.方法為ZBimageNamed,不是ZBimageNamed11

#import "ViewController.h"
#import "UIImage+Image.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
   
    [UIImage imageNamed:@"123"];//內(nèi)部調(diào)用ZBimageNamed方法

UIImage+Image.h文件


#import <UIKit/UIKit.h>

@interface UIImage (Image)


// 加載圖片  只要是系統(tǒng)底層不存在的類一定要聲明
//+ (UIImage *)ZBimageNamed:(NSString *)name;

@end

UIImage+Image.m文件

@implementation UIImage (Image)
// 加載分類到內(nèi)存的時候調(diào)用
+ (void)load
{
    // 交換方法

    // 獲取ZBimageNamed方法地址 
    //ZBimageNamed11只是用來交換地址用的,交換萬完之后,就沒有任何意義了,即以后用到的是ZBimageNamed,而不是ZBimageNamed11
    Method ZBimageNamed11 = class_getClassMethod(self, @selector(ZBimageNamed:));

    // 獲取imageNamed方法地址
    Method imageName11 = class_getClassMethod(self, @selector(imageNamed:));

    // 交換方法地址,相當于交換實現(xiàn)方式
    method_exchangeImplementations(ZBimageNamed11, imageName11);


}

// 不能在分類中重寫系統(tǒng)方法imageNamed,因為會把系統(tǒng)的功能給覆蓋掉,而且分類中不能調(diào)用super.
  
+ (instancetype)ZBimageNamed:(NSString *)name
{

    // 這里調(diào)用ZBimageNamed,本質(zhì)調(diào)用的是imageNamed
    UIImage *image = [self ZBimageNamed:name];

    if (image == nil) {
        NSLog(@"加載空的圖片");
    }

    return image;
}


@end


在.h中要不要寫方法的聲明 大解析:

  • 只要是系統(tǒng)底層不存在某個方法,當我們要調(diào)用這個方法時,一定要聲明。
  • 只要是A類不具有B方法或者B屬性,但是C類具有B方法或者B屬性,當我們用A類或者A的對象調(diào)用B的方法或者屬性時(調(diào)用屬性是調(diào)用set方法),必須要在A類的.h文件中聲明B方法或者屬性,還要在A的.m文件實現(xiàn)對應的方法(在本頁搜 動態(tài)添加屬性、普通添加屬性)
    • 1.子類繼承父類的類,重寫父類的方法,不需要聲明該方法,只需要實現(xiàn)該方法.
    • 2.給系統(tǒng)的類擴充方法,擴充的方法如果系統(tǒng)底層不存在,那么不僅要聲明擴充的方法,還要實現(xiàn)擴充的方法.
  • 對1和2的詳細解釋:
    • ZBImage子類繼承父類UIImage,如果子類重寫父類(系統(tǒng)底層)的+ (UIImage *)imageNamed:(NSString *)name方法,那么ZBImage類不需要在自己的.h文件中聲明系統(tǒng)的這個方法,只需在.m文件中重寫父類的這個方法,最后只需要在外界用UIImage調(diào)用這個類方法即可.
    • UIImage添加分類,為的是給UIImage類擴充新功能.那么不僅要在分類的.h文件聲明方法+ (UIImage *)ZBimageNamed:(NSString *)name;,還要在.m文件中實現(xiàn)該方法,最后只需在外界用UIImage調(diào)用這個類方法即可.否則提示在接口中沒有類方法(接口就是.h文件中聲明的方法)

拓展:基礎(chǔ)知識點

  • 類的本質(zhì):
    • 類本身也是一個對象,是class類型的對象,簡稱“類對象”。類名就代表著類對象,每個類只有一個類對象
    • 獲取內(nèi)存中的類對象.例如[Person class]
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • 對于從事 iOS 開發(fā)人員來說,所有的人都會答出【runtime 是運行時】什么情況下用runtime?大部分人能...
    夢夜繁星閱讀 3,821評論 7 64
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,906評論 33 466
  • 導讀:11、12月注定是不太平的月份,好多小型互聯(lián)網(wǎng)創(chuàng)業(yè)公司都突然崩塌,最近一個朋友跟我抱怨道,說終于感受到了互聯(lián)...
    柳駿閱讀 9,885評論 11 166
  • 強調(diào)自己的不幸,放大自己的委屈,自以為是的特殊性,統(tǒng)統(tǒng)都是懦夫的體現(xiàn)。
    陳鄉(xiāng)閱讀 160評論 0 0
  • 針對大功率密度的應用場合,比如激光、服務器、光伏能源、醫(yī)療設(shè)備、軍工設(shè)備中,由于對溫度的苛刻要求,水冷板就成了唯一...
    sanre123閱讀 6,469評論 0 1

友情鏈接更多精彩內(nèi)容