iOS進(jìn)階補(bǔ)完計(jì)劃--通讀runtime(.h)

說到runtime。所有iOS的開發(fā)者無不知曉。運(yùn)行時(shí)、swizzle、黑魔法等等。
不過用的時(shí)候是copy代碼、還是真正理解了runtime以及OC中類、對象、方法的本質(zhì)結(jié)構(gòu)。
起碼就我而言、很長一段時(shí)間(以年來計(jì)算)。都是前者。

所以這篇文章不屬于教學(xué)貼。希望借此能在runtime、以及OC的本質(zhì)方面更深一步。

這里有一篇很不錯(cuò)的入門文章、內(nèi)部對runtime進(jìn)行了蠻詳細(xì)的歸納總結(jié)。并且還列舉了很多runtime的實(shí)用帖子鏈接。
iOS 模塊詳解—「Runtime面試、工作」看我就 ?? 了 _

目錄

  • 類和對象
    • 實(shí)例對象(id)
    • 類對象(Class)
    • 元類(Meta Class)
    • 類與對象的總結(jié)
  • runtime正題
  • runtime方法的前綴
  • 對象操作方法(object_)
  • 全局操作方法(objc_)
  • 類操作方法(class_)
  • 動態(tài)創(chuàng)建類和對象
    • 動態(tài)創(chuàng)建類
    • 動態(tài)創(chuàng)建對象
  • 方法操作(method_)
  • 實(shí)例變量操作(ivar_)
    • 通過偏移量ivar_offset、獲取/修改任意一個(gè)成員變量。
  • 屬性操作(property_)
    • 屬性的動態(tài)添加與獲取
  • 協(xié)議操作(protocol_)
    • 動態(tài)的創(chuàng)建一個(gè)協(xié)議
  • 庫操作
  • 選擇器操作(sel_)
  • 語言特性
  • 關(guān)聯(lián)策略
  • 編碼類型

類和對象

為什么runtime的博客、要聊究類和對象?
或許寫出一段swizzle代碼更直觀

- (void)runtime_swizzle_test {
    Method method1 = class_getInstanceMethod([self class], @selector(func1));
    Method method2 = class_getInstanceMethod([self class], @selector(func2));
    method_exchangeImplementations(method1, method2);
}

所有的runtime方法、只要與方法操作相關(guān)。
無一例外都需要使用Method或者其結(jié)構(gòu)體內(nèi)部的IMP、SEL等指針。
而這個(gè)Method恰恰存放在的結(jié)構(gòu)體中。

除此之外:

  • class_getInstanceMethod(獲取Method)、class_copyIvarList(獲取屬性列表)。也都是建立在class_也就是類操作之下。
  • object_setIvar(屬性賦值)。則是建立在object_、也就是對象操作之下。

所以、大概可以知道了解類和對象對理解runtime有多么大的幫助了。

我們每天都在創(chuàng)建類、或者類的實(shí)例對象。但類和實(shí)例對象到底是什么?

[XXX new]或者[[XXX alloc]init]就是實(shí)例對象?
說得對、但是這淺顯了。相信如果面試官如此提問、你絕壁不敢這么回答的。

  • 實(shí)例對象(id)
id obj = self;

這就是我們的OC對象。

/// A pointer to an instance of a class.
/// 指向一個(gè)實(shí)例的指針
typedef struct objc_object *id;


/// Represents an instance of a class.
/// 表示類的實(shí)例。
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};
  • 實(shí)例對象在被創(chuàng)建的過程中會拷貝實(shí)例所屬的類的成員變量、但并不拷貝類定義的方法
  • isa
    整個(gè)id對象的結(jié)構(gòu)體內(nèi)部、只有一個(gè)isa指針、指向了其所屬的Class。
  • 調(diào)用實(shí)例方法時(shí)。
    系統(tǒng)會根據(jù)實(shí)例的isa指針去類的方法列表及父類的方法列表中尋找與消息對應(yīng)的selector指向的方法。
  • 類對象(Class)
Class class = [self class];

這就是我們每天都在使用的Class、也就是類。點(diǎn)進(jìn)去

#import <objc/objc.h>
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

可以看到Class本質(zhì)是一個(gè)objc_class的結(jié)構(gòu)體。繼續(xù)
這里為了看著舒服、我把一些標(biāo)注的宏刪掉了。

struct objc_class {
    Class  isa ;  // 指向所屬類的指針(_Nonnull)
    Class super_class;  // 父類(_Nullable)
    const char *  name; // 類名(_Nonnull)
    long version;  // 類的版本信息(默認(rèn)為0)
    long info;  // 類信息(供運(yùn)行期使用的一些位標(biāo)識)
    long instance_size;  // 該類的實(shí)例變量大小
    struct objc_ivar_list * ivars;  // 該類的成員變量鏈表(_Nullable)
    struct objc_method_list ** methodLists ;  // 方法定義的鏈表(_Nullable)
    struct objc_cache * cache  // 方法緩存(_Nonnull)
    struct objc_protocol_list * protocols;  // 協(xié)議鏈表(_Nullable)
} ;
  • isa
    Class的結(jié)構(gòu)體中也有isa指針、但和對象不同的是。Classisa指向的是Class所屬的類、即元類。
  • 調(diào)用類方法時(shí)
    系統(tǒng)會通過該isa指針從元類中尋找方法對應(yīng)的函數(shù)指針。
  • super_class
    父類指針、如果該類已經(jīng)是最頂層的根類(如NSObject或NSProxy)、則 super_class為NULL。
  • ivars
    objc_ivar_list類型的指針,用來存儲這個(gè)類中所有成員變量的信息。
//變量列表
struct objc_ivar_list {
    int ivar_count ; 
#ifdef __LP64__
    int space ;
#endif
    /* variable length structure */
    struct objc_ivar ivar_list[1] ;
}

//單個(gè)變量信息
struct objc_ivar {
    char * _Nullable ivar_name ;
    char * _Nullable ivar_type ;
    int ivar_offset ; //基地址偏移字節(jié)
#ifdef __LP64__
    int space ;
#endif
}             
  • methodLists
//方法列表
struct objc_method_list {
    struct objc_method_list * _Nullable obsolete ;

    int method_count  ;
#ifdef __LP64__
    int space ;
#endif
    /* variable length structure */
    struct objc_method method_list[1]  
}   
//單個(gè)方法的信息
struct objc_method {
    SEL _Nonnull method_name ;
    char * _Nullable method_types ;
    IMP _Nonnull method_imp  ;
} 
  • objc_cache
struct objc_cache {
    unsigned int mask /* total = mask + 1 */                 OBJC2_UNAVAILABLE;
    unsigned int occupied                                    OBJC2_UNAVAILABLE;
    Method buckets[1]                                        OBJC2_UNAVAILABLE;
};

用于緩存最近使用的方法。系統(tǒng)在調(diào)用方法時(shí)會先去cache中查找、在沒有查找到時(shí)才會去methodLists中遍歷獲取需要的方法。

  • 元類(Meta Class)

上面我們看到了。在objc_class中也有一個(gè)isa指針、這說明Class類本身也是一個(gè)對象。為了處理類和對象的關(guān)系、Runtime 庫創(chuàng)建了一種叫做Meta Class(元類) 的東西、類對象所屬的類就叫做元類。Meta Class表述了類對象本身所具備的元數(shù)據(jù)。

  • 元類用來儲存類方法
  • 元類也是一個(gè)對象、所以才能調(diào)用他的方法。
  • 元類的元類、為根元類。
  • 根元類的元類、是其本身。

更多的關(guān)于元類的就暫且打住~畢竟對于runtime。了解這些元類和類的關(guān)系已經(jīng)夠用了。

  • 類與對象的總結(jié)
類與對象

還有一張?zhí)貏e經(jīng)典的圖


類關(guān)系示意圖
  • 規(guī)則一: 實(shí)例對象的isa指向該類、類的isa指向元類(metaClass)。

  • 規(guī)則二: 類的superClass指向其父類、如果該類為根類則值為nil。

  • 規(guī)則三: 元類的isa指向根元類、如果該元類是根元類則指向自身。

  • 規(guī)則四: 元類的superClass指向父元類、若根元類則指向該根類。


runtime正題

runtime方法的前綴

runtime方法有很多、最方便歸納的方式就是通過前綴來確定其作用對象。

對象操作方法(object_)

包含了對對象的所有操作。
拷貝、釋放、獲取所屬類(isa)、所屬成員變量的操作(初始化的時(shí)候會copy成員變量列表)

/** 
 * 返回給定對象的副本。
 */
id object_copy(id _Nullable obj, size_t size)
/** 
 * 釋放對象內(nèi)存
 */
id object_dispose(id _Nullable obj)
/** 
 * 返回對象的類--也就是上文說的isa指針?biāo)傅念? */
Class object_getClass(id _Nullable obj) 
/** 
 * 為對象設(shè)置一個(gè)新的類
 * 其實(shí)就等于 NSArray  *arr = [NSMutableArray new];
 * 這個(gè)arr依舊是_M類型、但是已經(jīng)不能使用addobj方法了、起碼在編譯階段是。
 */
Class object_setClass(id _Nullable obj, Class _Nonnull cls) 
/** 
 * 判斷一個(gè)對象是否為Class類型的對象
 */
BOOL object_isClass(id _Nullable obj)
/** 
 * 返回一個(gè)對象中指定實(shí)例變量(Ivar)的值
 */
id object_getIvar(id _Nullable obj, Ivar _Nonnull ivar) 
/*
* 為對象設(shè)置一個(gè)實(shí)例變量的值
*/
void object_setIvar(id _Nullable obj, Ivar _Nonnull ivar, id _Nullable value) 
/*
*  10.0以后多了一個(gè)方法
*/
void object_setIvarWithStrongDefault(id _Nullable obj, Ivar _Nonnull ivar,
                                id _Nullable value) 
/*
* 非ARC下的兩個(gè)set方法
*/
void object_setInstanceVariable(id _Nullable obj, const char * _Nonnull name,
                           void * _Nullable value);
void object_setInstanceVariableWithStrongDefault(id _Nullable obj,
                                            const char * _Nonnull name,
                                            void * _Nullable value)
/*
* 非ARC下的get方法
*/
Ivar object_getInstanceVariable(id _Nullable obj, const char * _Nonnull name,
                           void * _Nullable * _Nullable outValue)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)
    OBJC_ARC_UNAVAILABLE;
其中有比較特殊的:
  • Class object_getClass(id _Nullable obj)
    這個(gè)方法可以獲得對象結(jié)構(gòu)體內(nèi)部isa指針?biāo)?、也就是對象所屬的類?具體是啥可以翻回去看)。
    但需要注意的是、這個(gè)和我們平時(shí)用的[self class]方法、并不完全等價(jià)。
+ (Class)class {
    return self; // 返回自身指針
}
- (Class)class {
    return object_getClass(self); // 調(diào)用'object_getClass'返回isa指針
}

也就是說、通過[Class object_getClass]是可以獲取元類的、但是通過[Class class]則不行。

  • void object_setIvar(id _Nullable obj, Ivar _Nonnull ivar, id _Nullable value)
    為對象設(shè)置一個(gè)實(shí)例變量的值
    但依舊不能為不存在的變量賦值
    當(dāng)獲取一個(gè)不存在實(shí)例變量時(shí)、Ivar將等于0x0000000000000000。自然沒辦法對其進(jìn)行賦值。

全局操作方法(objc_)

這里的方法并不基于對象、也不需要以對象作為參數(shù)。
基本都是一些類相關(guān)的獲取。

/*
* 返回指定的Class
* 如果未注冊命名類的定義,則該函數(shù)調(diào)用類處理程序。
* 然后再次檢查是否注冊了類。
*/
Class  objc_getClass(const char * _Nonnull name)
/*
* 返回指定的元類Class
*/
Class objc_getMetaClass(const char * _Nonnull name)
/*
* 返回指定的Class
* 不調(diào)用類處理程序回調(diào)。
*/
Class objc_lookUpClass(const char * _Nonnull name)
/*
* 返回指定的Class
* 如果沒有找到類,就會殺死進(jìn)程。
*/
Class objc_getRequiredClass(const char * _Nonnull name)
/*
* 獲取所有已經(jīng)注冊類的列表
*/
int objc_getClassList(Class _Nonnull * _Nullable buffer, int bufferCount)
/*
* 獲取所有已經(jīng)注冊類的實(shí)例列表
*/
Class *objc_copyClassList(unsigned int * _Nullable outCount)
其中有比較特殊的:
  • 獲取所有已經(jīng)注冊的類--數(shù)量.
int objc_getClassList(Class _Nonnull * _Nullable buffer, int bufferCount)
int numClasses = 0, newNumClasses = objc_getClassList(NULL, 0);
Class *classes = NULL;
while (numClasses < newNumClasses) {
    numClasses = newNumClasses;
    classes = (Class *)realloc(classes, sizeof(Class) * numClasses);
    newNumClasses = objc_getClassList(classes, numClasses);
    
    for (int i = 0; i < numClasses; i++) {
        const char *className = class_getName(classes[i]);
        if (class_getSuperclass(classes[i]) == [UIScrollView class]) {
            //打印所有UIScrollView的子類
            NSLog(@"subclass of UIScrollView : %s", className);
        }
    }
}
free(classes);
  • 獲取所有已經(jīng)注冊的類--實(shí)例列表.
Class *objc_copyClassList(unsigned int * _Nullable outCount)
unsigned int outCount;
Class *classes = objc_copyClassList(&outCount);
for (int i = 0; i < outCount; i++) {
    if (class_getSuperclass(classes[i]) == [UIScrollView class]) {
        //打印所有UIScrollView的子類
        NSLog(@"subclass of UIScrollView : %s", class_getName(classes[i]));
    }
}
free(classes);

類對象操作方法(class_)

這里的所有操作都針對類對象

/** 
 * 返回類的名稱
 */
char * class_getName(Class _Nullable cls) 
/** 
 * 判斷該類對象是否為元類
 */
BOOL class_isMetaClass(Class _Nullable cls) 
/** 
 * 獲取結(jié)構(gòu)體中的super_class、也就是父類對象指針
 */
Class class_getSuperclass(Class _Nullable cls) 
/** 
 * 給一個(gè)類對象設(shè)置新的父類指針
 * 返回舊的父類
 * 這個(gè)方法已經(jīng)被棄用并且apple明確指出不應(yīng)該使用。
 */
Class class_setSuperclass(Class _Nonnull cls, Class _Nonnull newSuper) 
/** 
 * 獲取類對象的版本號
 * 沒發(fā)現(xiàn)啥用處、不過測試起來
 * 所有的元類都為7
 * 普通類NSDateFormatter最高(41)、并且大于5的只有它一個(gè)
 */
int class_getVersion(Class _Nullable cls)
/** 
 * 為一個(gè)類設(shè)置版本號
 */
void class_setVersion(Class _Nullable cls, int version)
/** 
 * 獲取一個(gè)類的實(shí)例大小
 */
size_t class_getInstanceSize(Class _Nullable cls)
/** 
 * 獲取類中指定名稱實(shí)例成員變量的信息(Ivar)
 */
Ivar class_getInstanceVariable(Class _Nullable cls, const char * _Nonnull name)
/** 
 * 獲取類"成員變量"的信息(Ivar)
 * 但是好像OC中沒有這種東西
 */
Ivar class_getClassVariable(Class _Nullable cls, const char * _Nonnull name) 
/** 
 * 獲取全部成員變量列表
 * 但是不包括父類
 */
Ivar * class_copyIvarList(Class _Nullable cls, unsigned int * _Nullable outCount)
/** 
 * 查找某個(gè)實(shí)例方法
 */
Method class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name)
/** 
 * 查找某個(gè)類方法
 */
Method class_getClassMethod(Class _Nullable cls, SEL _Nonnull name)
/** 
 * 查找某個(gè)實(shí)例方法的實(shí)現(xiàn)(IMP)
 */
IMP class_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name) 
/** 
 * 查找某個(gè)實(shí)例方法的實(shí)現(xiàn)(IMP)
 * 不過在“ arm64”也就是現(xiàn)在的機(jī)器上報(bào)報(bào)紅
 */
IMP class_getMethodImplementation_stret(Class _Nullable cls, SEL _Nonnull name) 
/** 
 * 查找某個(gè)實(shí)例方法的實(shí)現(xiàn)(IMP)
 * 不過在“ arm64”也就是現(xiàn)在的機(jī)器上報(bào)報(bào)紅
 */
BOOL class_respondsToSelector(Class _Nullable cls, SEL _Nonnull sel) 
/** 
 * 返回該類所有方法(結(jié)構(gòu)體中的objc_method_list)
 */
Method * class_copyMethodList(Class _Nullable cls, unsigned int * _Nullable outCount)
/** 
 * 判斷類是否實(shí)現(xiàn)了某個(gè)協(xié)議
 * 其實(shí)就是[self conformsToProtocol:@protocol(UITableViewDelegate)]的底層
 */
BOOL class_conformsToProtocol(Class _Nullable cls, Protocol * _Nullable protocol) 
/** 
 * 返回該類的協(xié)議列表(結(jié)構(gòu)體中的objc_protocol_list)
 */
Protocol * * class_copyProtocolList(Class _Nullable cls, unsigned int * _Nullable outCount)
/** 
 * 返回該類的指定屬性(注意不是成員變量)
 */
objc_property_t class_getProperty(Class _Nullable cls, const char * _Nonnull name)
/** 
 * 返回該類的屬性列表
 */
objc_property_t * class_copyPropertyList(Class _Nullable cls, unsigned int * _Nullable outCount)
/** 
* 返回該類成員變量的布局 
*/
uint8_t * class_getIvarLayout(Class _Nullable cls)
/** 
* 返回該類weak成員變量的布局 
*/
uint8_t * class_getWeakIvarLayout(Class _Nullable cls)
/** 
* 添加方法 如果返回YES則添加成功
*/
BOOL class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, 
                const char * _Nullable types) 
/** 
* 替換方法實(shí)現(xiàn)(IMP)
* 如果之前存在該方法--直接替換
* 如果之前不存在該方法--添加方法
*/
IMP class_replaceMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, 
                    const char * _Nullable types) 
/** 
* 添加成員變量
* 這個(gè)函數(shù)只能在objc_allocateClassPair和objc_registerClassPair之前調(diào)用。
* 也間接的說明、已經(jīng)注冊的類不能動態(tài)的添加成員變量
*/
BOOL class_addIvar(Class _Nullable cls, const char * _Nonnull name, size_t size, 
              uint8_t alignment, const char * _Nullable types) 
/** 
* 添加協(xié)議
*/
BOOL class_addProtocol(Class _Nullable cls, Protocol * _Nonnull protocol) 
/** 
* 添加屬性
* 注意這里屬性的意義。和Category中的一樣、并不包含成員變量和setget方法
*/
BOOL class_addProperty(Class _Nullable cls, const char * _Nonnull name,
                  const objc_property_attribute_t * _Nullable attributes,
                  unsigned int attributeCount)
/** 
* 替換屬性
* 注意這里屬性的意義。和Category中的一樣、并不包含成員變量和setget方法
*/
void class_replaceProperty(Class _Nullable cls, const char * _Nonnull name,
                      const objc_property_attribute_t * _Nullable attributes,
                      unsigned int attributeCount)
/** 
* 設(shè)置變量布局
*/
void class_setIvarLayout(Class _Nullable cls, const uint8_t * _Nullable layout)
/** 
* 設(shè)置weak變量布局
*/
void class_setWeakIvarLayout(Class _Nullable cls, const uint8_t * _Nullable layout)
/** 
* 由CoreFoundation的toll-free橋接使用。
* 不能主動調(diào)用
*/
Class objc_getFutureClass(const char * _Nonnull name) 
其中有比較特殊的:
  • class_getSuperclass[self superclass]
    class_getSuperclass等價(jià)于[self superclass]都直接獲取指向父類對象的指針。
    并且由之前的結(jié)構(gòu)圖(類關(guān)系示意圖)可知
    1、元類的元類為NSObject
    2、元類的父類為正常結(jié)構(gòu)的父類
    可以驗(yàn)證一下
NSLog(@"類對象--%@",[self class]);
NSLog(@"父類對象--%@",[self superclass]);
NSLog(@"父類對象--%@",[[self superclass] superclass]);
NSLog(@"父類對象--%@",[[[self superclass] superclass] superclass]);
NSLog(@"父類對象--%@",[[[[self superclass] superclass] superclass] superclass]);

//獲取self的類 ==》 ViewController(類)  再獲取其元類 ViewController(元類)
Class class = object_getClass(object_getClass(self));
NSLog(@"元類對象--%@",class);
NSLog(@"如果再繼續(xù)獲取元類---%@",object_getClass(class));
NSLog(@"父元類對象--%@",class_getSuperclass(class));
NSLog(@"父元類對象--%@",class_getSuperclass(class_getSuperclass(class)));
NSLog(@"父元類對象--%@",class_getSuperclass(class_getSuperclass(class_getSuperclass(class))));
NSLog(@"父類對象--%@",class_getSuperclass(class_getSuperclass(class_getSuperclass(class_getSuperclass(class)))));
NSLog(@"父類對象--%@",class_getSuperclass(class_getSuperclass(class_getSuperclass(class_getSuperclass(class_getSuperclass(class))))));

打印結(jié)果:

類對象--ViewController
父類對象--UIViewController
父類對象--UIResponder
父類對象--NSObject
父類對象--(null)
元類對象--ViewController
如果再繼續(xù)獲取元類---NSObject
父元類對象--UIViewController
父元類對象--UIResponder
父元類對象--NSObject
父類對象--NSObject
父類對象--(null)

動態(tài)創(chuàng)建類和對象

  • 動態(tài)創(chuàng)建類
/** 
*  創(chuàng)建一個(gè)新的類、以及其元類
*/
Class objc_allocateClassPair(Class _Nullable superclass, const char * _Nonnull name, 
                       size_t extraBytes) 
/** 
*  注冊一個(gè)類
*/
void objc_registerClassPair(Class _Nonnull cls) 
/** 
*  用于KVO
*  禁止主動調(diào)用
*/
Class objc_duplicateClass(Class _Nonnull original, const char * _Nonnull name,
                    size_t extraBytes)
/** 
*  銷毀一個(gè)類
*  如果程序運(yùn)行中還存在類或其子類的實(shí)例,則不能調(diào)用針對類調(diào)用該方法
*/
void objc_disposeClassPair(Class _Nonnull cls) 

objc_allocateClassPair函數(shù):如果我們要?jiǎng)?chuàng)建一個(gè)根類,則superclass指定為Nil。extraBytes通常指定為0,該參數(shù)是分配給類和元類對象尾部的索引ivars的字節(jié)數(shù)。
為了創(chuàng)建一個(gè)新類,我們需要調(diào)用objc_allocateClassPair。
然后使用諸如class_addMethod,class_addIvar等函數(shù)來為新創(chuàng)建的類添加方法、實(shí)例變量和屬性等。
完成這些后,我們需要調(diào)用objc_registerClassPair函數(shù)來注冊類,之后這個(gè)新類就可以在程序中使用了。
有兩點(diǎn)需要注意:

  • 實(shí)例方法和實(shí)例變量應(yīng)該添加到類自身上,而類方法應(yīng)該添加到類的元類上。
  • class_addIvar必須使用在類生成后objc_allocateClassPair、注冊前objc_registerClassPair。因?yàn)槌绦虿辉试S一個(gè)已經(jīng)注冊、分配好內(nèi)存的類進(jìn)行擴(kuò)容。
    ※※※※這也是為什么類別、添加了屬性卻不能添加成員變量的原因

舉個(gè)例子:

Class cls = objc_allocateClassPair(MyClass.class, "MySubClass", 0);
class_addMethod(cls, @selector(submethod1), (IMP)imp_submethod1, "v@:");
class_replaceMethod(cls, @selector(method1), (IMP)imp_submethod1, "v@:");
class_addIvar(cls, "_ivar1", sizeof(NSString *), log(sizeof(NSString *)), "i");
 
objc_property_attribute_t type = {"T", "@\"NSString\""};
objc_property_attribute_t ownership = { "C", "" };
objc_property_attribute_t backingivar = { "V", "_ivar1"};
objc_property_attribute_t attrs[] = {type, ownership, backingivar};
 
class_addProperty(cls, "property2", attrs, 3);
objc_registerClassPair(cls);
 
id instance = [[cls alloc] init];
[instance performSelector:@selector(submethod1)];
[instance performSelector:@selector(method1)];
  • 動態(tài)創(chuàng)建對象
/** 
* 實(shí)例化一個(gè)類對象
* 基本等價(jià)于[XXX new]
* 第二個(gè)參數(shù)表示分配的額外字節(jié)數(shù)。這些額外的字節(jié)可用于存儲在類定義中所定義的實(shí)例變量之外的實(shí)例變量。
*/
id class_createInstance(Class _Nullable cls, size_t extraBytes)
/** 
* 在指定位置實(shí)例化一個(gè)類對象
*/
id objc_constructInstance(Class _Nullable cls, void * _Nullable bytes) 
/** 
* 銷毀一個(gè)類的實(shí)例
* 但不會釋放并移除任何與其相關(guān)的引用
*/
void * _Nullable objc_destructInstance(id _Nullable obj) 

方法操作(method_)

大概我們應(yīng)該先了解一下Method

struct objc_method {
    SEL _Nonnull method_name;  //方法名、借此才能從方法列表鎖定單個(gè)Method
    char * _Nullable method_types; //方法返回值以及參數(shù)的描述
    IMP _Nonnull method_imp; //方法實(shí)現(xiàn).(方法實(shí)現(xiàn)所在的位置)
} 

Method包含了一個(gè)方法所需要的全部要素。并且可以自由獲取任意其一。

/** 
* 返回方法的名稱
*/
SEL method_getName(Method _Nonnull m) 
/** 
* 返回方法的實(shí)現(xiàn)(IMP)
*/
IMP method_getImplementation(Method _Nonnull m) 
/** 
* 返回描述方法參數(shù)和返回類型的字符串
*/
char * method_getTypeEncoding(Method _Nonnull m) 
/** 
* 返回方法參數(shù)個(gè)數(shù)
* 參數(shù)最低為兩個(gè)(每個(gè)方法都有self以及_cmd兩個(gè)隱性參數(shù))
*/
int method_getNumberOfArguments(Method _Nonnull m)
/** 
* 返回方法返回類型
* 類型會傳遞給第二個(gè)參數(shù)"dst"
*/
void method_getReturnType(Method _Nonnull m, char * _Nonnull dst, size_t dst_len)
/** 
* 返回方法返回類型
* 第二個(gè)參數(shù)index為參數(shù)位置、類型會傳遞給第三個(gè)參數(shù)"dst"(越位會返回'\0')
*/
void method_getArgumentType(Method _Nonnull m, unsigned int index, 
                       char * _Nullable dst, size_t dst_len) 
/** 
* 為一個(gè)Method設(shè)置具體實(shí)現(xiàn)(IMP)
*/
IMP method_setImplementation(Method _Nonnull m, IMP _Nonnull imp) 
/** 
* ※※※大名鼎鼎的swizzle※※※
*/
void method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2) 

能說的不多:

  • swizzle的本質(zhì)?

method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)的注釋里說很明白了.

 *  IMP imp1 = method_getImplementation(m1);
 *  IMP imp2 = method_getImplementation(m2);
 *  method_setImplementation(m1, imp2);
 *  method_setImplementation(m2, imp1);

本質(zhì)就是交換了兩個(gè)方法的實(shí)現(xiàn)。
所以說用method_setImplementation設(shè)置IMP的話、也可以實(shí)現(xiàn)一個(gè)IMP實(shí)現(xiàn)對應(yīng)多個(gè)SEL。

實(shí)例變量操作(ivar_)

Method一樣。我們也來看看ivar的結(jié)構(gòu)體、看看ivar能做什么。


/// An opaque type that represents an instance variable.
typedef struct objc_ivar *Ivar;

struct objc_ivar {
    char * _Nullable ivar_name; //變量名
    char * _Nullable ivar_type; //變量類型
    int ivar_offset  //基地址偏移字節(jié)
#ifdef __LP64__
    int space ; //占用空間
#endif
}       

實(shí)例變量操作的API特別少(因?yàn)楂@取/設(shè)置變量的值都由ojbect_進(jìn)行)

/** 
* 返回實(shí)例變量的名稱
*/
char * _Nullable ivar_getName(Ivar _Nonnull v) 
/** 
* 返回實(shí)例變量的類型
*/
char * _Nullable ivar_getTypeEncoding(Ivar _Nonnull v) 
/** 
* 返回實(shí)例變量的偏移量
*/
ptrdiff_t ivar_getOffset(Ivar _Nonnull v) 
  • 基地址偏移字節(jié)ivar_offset
    • 先聊聊類的布局
      在編譯我們的類時(shí)、編譯器生成了一個(gè) ivar布局、顯示了在類中從哪可以訪問我們的 ivars 。如下圖:

      我們增加了父類的ivar、這個(gè)時(shí)候布局就出錯(cuò)了。

      雖然runtime可以在類加載的時(shí)候通過Non Fragile ivars進(jìn)行重新布局。
但是?。?!、對已經(jīng)加載完成(注冊好)的類、增加成員變量一樣會導(dǎo)致布局出錯(cuò)。

這也是為什么類別不能添加屬性的根本原因。

  • 通過偏移量ivar_offset、獲取/修改任意一個(gè)成員變量。

我們可以通過對象地址 + ivar偏移字節(jié)的方法、獲取任意一個(gè)成員變量。

@interface Person : NSObject
{
    @private int age_int;
    @private NSString * age_str;
    @public int age_public;
}
@end

@implementation Person
- (instancetype)init
{
    self = [super init];
    if (self) {
        age_public = 1;
        age_int = 1;
        age_str = @"1";
    }
    return self;
}

- (NSString *)description
{
    NSLog(@"person pointer==%p", self);
    NSLog(@"age_int pointer==%p", &age_int);
    return [NSString stringWithFormat:@"age_int==%d", age_int];
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        //私有成員變量--基本類型、可以使用偏移量
        Person * person = [Person new];
        Ivar age_int_ivar = class_getInstanceVariable(object_getClass(person), "age_int");
        int *age_int_pointer = (int *)((__bridge void *)(person) + ivar_getOffset(age_int_ivar));
        
        
        NSLog(@"age_int offset==%td", ivar_getOffset(age_int_ivar));
        
        * age_int_pointer = 10;
        NSLog(@"%@",person);
    }
    return 0;
}

打印結(jié)果:

age_int offset==8
person pointer==0x10061b070
age_int pointer==0x10061b078
age_int==10

屬性操作(property_)

/** 
 * 返回屬性名
 */
char * _Nonnull property_getName(objc_property_t _Nonnull property) 
/** 
 * 返回屬性描述(也就是屬性的屬性)
 * @property (nonatomic ,readonly ,strong) NSObject * obj;
 * "T@\"NSObject\",R,N,V_obj"
 */
char * _Nullable property_getAttributes(objc_property_t _Nonnull property) 
/** 
 * 返回屬性描述數(shù)組
 */
objc_property_attribute_t * _Nullable
property_copyAttributeList(objc_property_t _Nonnull property,
                           unsigned int * _Nullable outCount)
/** 
 * 返回屬性描述(也就是屬性的屬性)
 */
char * _Nullable property_copyAttributeValue(objc_property_t _Nonnull property,
                            const char * _Nonnull attributeName)
  • 屬性的動態(tài)添加與獲取
    注意這里的添加和category一樣、有名無實(shí)。
objc_property_attribute_t type = { "T", "@\"NSString\"" };//屬性類型為NSString
objc_property_attribute_t ownership = { "C", "copy" }; // C = copy
objc_property_attribute_t backingivar  = { "V", "_littleName" };//_littleName為Person類的全局變量,這里是讓新屬性與之關(guān)聯(lián)
objc_property_attribute_t attrs[] = { type, ownership, backingivar };
class_addProperty([Person class], "littleName", attrs, 3);
//驗(yàn)證是否添加成功
objc_property_t pt =  class_getProperty([Person class], "littleName");
NSLog(@"property's name: %s", property_getName(pt));
NSLog(@"property'attribute:%s",property_getAttributes(pt));
NSLog(@"property'release:%s",property_copyAttributeValue(pt, "C"));
NSLog(@"property'type:%s",property_copyAttributeValue(pt, "T"));
NSLog(@"property'value:%s",property_copyAttributeValue(pt, "V"));

打印結(jié)果:

property's name: littleName
property'attribute:T@"NSString",Ccopy,V_littleName
property'release:copy
property'type:@"NSString"
property'value:_littleName

協(xié)議操作(protocol_)

/** 
 * 返回一個(gè)協(xié)議
 */
Protocol * _Nullable objc_getProtocol(const char * _Nonnull name)
/** 
 * 返回runtime已知的所有協(xié)議的數(shù)組
 */
Protocol ** _Nullable objc_copyProtocolList(unsigned int * _Nullable outCount)
/** 
 * 判斷一個(gè)協(xié)議是否遵循了另一個(gè)協(xié)議
 */
BOOL protocol_conformsToProtocol(Protocol * _Nullable proto,
                            Protocol * _Nullable other)
/** 
 * 判斷兩個(gè)協(xié)議是否相同
 */
BOOL protocol_isEqual(Protocol * _Nullable proto, Protocol * _Nullable other)
/** 
 * 返回協(xié)議的名稱
 */
char * _Nonnull protocol_getName(Protocol * _Nonnull proto)
/** 
 * 返回協(xié)議中指定方法的描述
 */
struct objc_method_description protocol_getMethodDescription(Protocol * _Nonnull proto, SEL _Nonnull aSel,
                              BOOL isRequiredMethod, BOOL isInstanceMethod)
/** 
 * 返回協(xié)議中指定規(guī)則的所有方法描述
 */
struct objc_method_description * _Nullable protocol_copyMethodDescriptionList(Protocol * _Nonnull proto,
                                   BOOL isRequiredMethod,
                                   BOOL isInstanceMethod,
                                   unsigned int * _Nullable outCount)
/** 
 * 返回協(xié)議的指定屬性
 */
objc_property_t protocol_getProperty(Protocol * _Nonnull proto,
                     const char * _Nonnull name,
                     BOOL isRequiredProperty, BOOL isInstanceProperty)
/** 
 * 返回協(xié)議中所有屬性的列表
 */
objc_property_t * protocol_copyPropertyList(Protocol * _Nonnull proto,
                          unsigned int * _Nullable outCount)
/** 
 * 返回協(xié)議中所有指定規(guī)則屬性的列表
 */
objc_property_t * protocol_copyPropertyList2(Protocol * _Nonnull proto,
                           unsigned int * _Nullable outCount,
                           BOOL isRequiredProperty, BOOL isInstanceProperty)
/** 
 * 返回協(xié)議所遵循的協(xié)議列表(比如`NSPortDelegate`遵循了`NSObject`)
 */
Protocol ** protocol_copyProtocolList(Protocol * _Nonnull proto,
                          unsigned int * _Nullable outCount)
/** 
 * 創(chuàng)建一個(gè)協(xié)議(未注冊)
 */
Protocol * objc_allocateProtocol(const char * _Nonnull name) 
/** 
 * 向協(xié)議中添加方法
 */
void protocol_addMethodDescription(Protocol * _Nonnull proto, SEL _Nonnull name,
                              const char * _Nullable types,
                              BOOL isRequiredMethod, BOOL isInstanceMethod) 
/** 
 * 向協(xié)議中添加依賴協(xié)議
 */
void protocol_addProtocol(Protocol * _Nonnull proto, Protocol * _Nonnull addition) 
/** 
 * 向協(xié)議中添屬性
 */
void protocol_addProperty(Protocol * _Nonnull proto, const char * _Nonnull name,
                     const objc_property_attribute_t * _Nullable attributes,
                     unsigned int attributeCount,
                     BOOL isRequiredProperty, BOOL isInstanceProperty)
/** 
 * 注冊一個(gè)協(xié)議
 */
void objc_registerProtocol(Protocol * _Nonnull proto) 
  • 動態(tài)的創(chuàng)建一個(gè)協(xié)議
Protocol * protocol = objc_getProtocol("KTDelegate");
NSLog(@"%@",protocol);

if (!protocol) {
    //創(chuàng)建
    protocol = objc_allocateProtocol("KTDelegate");
    protocol_addMethodDescription(protocol, @selector(protocol_func), "desc", YES, YES);
    protocol_addProtocol(protocol, objc_getProtocol("NSObject"));
    
    
    objc_property_attribute_t type = { "T", "@\"NSString\"" };//屬性類型為NSString
    objc_property_attribute_t ownership = { "C", "copy" }; // C = copy
    objc_property_attribute_t backingivar  = { "V", "_littleName" };
    objc_property_attribute_t attrs[] = { type, ownership, backingivar };
    
    protocol_addProperty(protocol, "_littleName", attrs, 3, YES, YES);
    //注冊
    objc_registerProtocol(protocol);
    NSLog(@"register_end");
}

protocol = objc_getProtocol("KTDelegate");
NSLog(@"protocol == %@",protocol);
NSLog(@"protocol_name == %s",protocol_getName(protocol));
NSLog(@"protocol_desc == %s",protocol_getMethodDescription(protocol, @selector(protocol_func), YES, YES));
NSLog(@"protocol_property == %s",property_getName(protocol_getProperty(protocol, "_littleName", YES, YES)));

打印結(jié)果:

(null)
register_end
protocol == <Protocol: 0x100462f80>
protocol_name == KTDelegate
protocol_desc == protocol_func
protocol_property == _littleName

庫操作

/** 
 * 返回所有加載在Objective-C框架和動態(tài)庫上的名稱
 * char ** arr = objc_copyImageNames(&num);
 */
char ** objc_copyImageNames(unsigned int * _Nullable outCount) 
/** 
 * 返回一個(gè)類所屬的動態(tài)庫名稱
 */
char * class_getImageName(Class _Nullable cls) 
/** 
 * 返回一個(gè)庫中所有類的名稱
 */
char ** objc_copyClassNamesForImage(const char * _Nonnull image,
                            unsigned int * _Nullable outCount) 

簡單舉個(gè)例子

char * libName = class_getImageName(objc_getClass("NSString"));
int num = 0;
char ** libarr = objc_copyClassNamesForImage(libName, &num);
for (int i = 0; i < num; i ++) {
    NSLog(@"%s",libarr[i]);
}

選擇器操作(sel_)

/** 
 * 返回一個(gè)選擇器所指定的方法名稱
 */
char * sel_getName(SEL _Nonnull sel)
/** 
 * 注冊一個(gè)方法選擇器
 */
SEL sel_registerName(const char * _Nonnull str)
/** 
 * 比較兩個(gè)選擇器
 * SEL sel1 = @selector(fun1:);
 * SEL sel2 = sel_registerName("fun1:");
 * BOOL b = sel_isEqual(sel1, sel2);
 */
BOOL sel_isEqual(SEL _Nonnull lhs, SEL _Nonnull rhs) 

語言特性

/** 
 * 當(dāng)一個(gè)obj突變發(fā)生時(shí),這個(gè)函數(shù)會拋出異常
 */
void objc_enumerationMutation(id _Nonnull obj) 
/** 
 * 突變處理回調(diào)
 */
void objc_setEnumerationMutationHandler(void (*_Nullable handler)(id _Nonnull )) 
/** 
 * objc_msgForward方法的調(diào)用
 */
void objc_setForwardHandler(void * _Nonnull fwd, void * _Nonnull fwd_stret) 
/** 
 * 就是直接把一個(gè)block作為IMP
 */
IMP imp_implementationWithBlock(id _Nonnull block)
/** 
 * 返回(使用imp_implementationWithBlock創(chuàng)建的)IMP對應(yīng)的block
 * 舉個(gè)例子:
    void (^block)(id ,SEL) = ^(id self ,SEL _sel){
         NSLog(@"%s",sel_getName(_sel));
    };
    IMP imp = imp_implementationWithBlock(block);
    class_addMethod([Person class], @selector(imp_fun), imp, "v@:@");
    [[Person new] performSelector:@selector(imp_fun)];
    void (^block2)(id ,SEL)= imp_getBlock(imp);
    block2([Person new],@selector(fun_block2));

    輸出:
    imp_fun
    fun_block2
 */
id imp_getBlock(IMP _Nonnull anImp);
/** 
 *   移除(使用imp_implementationWithBlock創(chuàng)建的)IMP對應(yīng)的block
 */
BOOL imp_removeBlock(IMP _Nonnull anImp)
/** 
 *   在表達(dá)式中使用__weak變量時(shí)將自動使用該函數(shù)
 *   該函數(shù)加載一個(gè)弱指針引用的對象,并在對其做retain和autoreleasing操作后返回它。這樣,對象就可以在調(diào)用者使用它時(shí)保持足夠長的生命周期
    UIView * view = [UIView new];
    __weak UIView * w_view = view;
    NSLog(@"retain  count = %ld\n",CFGetRetainCount((__bridge  CFTypeRef)(w_view)));
    objc_loadWeak(&w_view);
    NSLog(@"retain  count = %ld\n",CFGetRetainCount((__bridge  CFTypeRef)(w_view)));
    __strong UIView * s_view = w_view;
    __weak UIView * ww_view = w_view;
    NSLog(@"retain  count = %ld\n",CFGetRetainCount((__bridge  CFTypeRef)(w_view)));
 */
id objc_loadWeak(id _Nullable * _Nonnull location)
/** 
 *   可以用來為__weak變量賦值
 */
id objc_storeWeak(id _Nullable * _Nonnull location, id _Nullable obj) 

關(guān)聯(lián)策略

// 關(guān)聯(lián)策略
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {

    OBJC_ASSOCIATION_ASSIGN = 0,           //@property(assign)。
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, //@property(strong, nonatomic)
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,  //@property(copy, nonatomic)
    OBJC_ASSOCIATION_RETAIN = 01401,       //@property(strong,atomic)
    OBJC_ASSOCIATION_COPY = 01403         //@property(copy, atomic)
};

/** 
 *   將一個(gè)key&&value關(guān)聯(lián)到另一個(gè)對象上、并且設(shè)置關(guān)聯(lián)策略
 */
void objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
                         id _Nullable value, objc_AssociationPolicy policy)
/** 
 *   將關(guān)聯(lián)的對象取出
 */
id _Nullable objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
/** 
 *   移除關(guān)聯(lián)所有對象
 */
void objc_removeAssociatedObjects(id _Nonnull object)

這個(gè)就是我們經(jīng)常用的、給類別綁定屬性的

static char *key = "key";
objc_setAssociatedObject(self, &key, @"hello world", OBJC_ASSOCIATION_COPY_NONATOMIC);
NSString *associated = objc_getAssociatedObject(self, &key);
NSLog(@"objc_getAssociatedObject===%@",associated);

編碼類型

#define _C_ID       '@' // 代表對象類型
#define _C_CLASS    '#' // 代表類對象 (Class)
#define _C_SEL      ':' // 代表方法selector (SEL)
#define _C_CHR      'c' // 代表char類型
#define _C_UCHR     'C' // 代表unsigned char類型
#define _C_SHT      's' // 代表short類型
#define _C_USHT     'S' // 代表unsigned short類型
#define _C_INT      'i' // 代表int類型
#define _C_UINT     'I' // 代表unsigned int類型
#define _C_LNG      'l' // 代表long類型,在64位處理器上也是按照32位處理
#define _C_ULNG     'L' // 代表unsigned long類型
#define _C_LNG_LNG  'q' // 代表long long類型
#define _C_ULNG_LNG 'Q' // 代表unsigned long long類型
#define _C_FLT      'f' // 代表float類型
#define _C_DBL      'd' // 代表double類型
#define _C_BFLD     'b' //
#define _C_BOOL     'B' // 代表C++中的bool或者C99中的_Bool
#define _C_VOID     'v' // 代表void類型
#define _C_UNDEF    '?' // 代表未知類型
#define _C_PTR      '^' // 代表指針類型
#define _C_CHARPTR  '*' // 代表字符串類型 (char *)
#define _C_ATOM     '%' //
#define _C_ARY_B    '[' // 代表array
#define _C_ARY_E    ']'
#define _C_UNION_B  '(' // 代表UNION
#define _C_UNION_E  ')'
#define _C_STRUCT_B '{' // 代表結(jié)構(gòu)體
#define _C_STRUCT_E '}'
#define _C_VECTOR   '!' // 代表矢量類型
#define _C_CONST    'r' // 代表常量類型

最后

本文主要是自己的學(xué)習(xí)與總結(jié)。如果文內(nèi)存在紕漏、萬望留言斧正。如果不吝賜教小弟更加感謝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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