Runtime

 typedef struct objc_class *Class;    //類在Objective-C中就是結構體指針

 struct objc_class {
     Class _Nonnull isa  OBJC_ISA_AVAILABILITY;   //isa 指向元類的objc_class結構體指針,iOS中的類也是對象,元類中儲存有類對象的類方法
 
     #if !__OBJC2__
     Class _Nullable super_class                              OBJC2_UNAVAILABLE;        //指向父類的objc_class結構體指針,可以通過父類的指針找到變量和方法
     const char * _Nonnull name                               OBJC2_UNAVAILABLE;        //類名
     long version                                             OBJC2_UNAVAILABLE;        //版本號,默認爲0
     long info                                                OBJC2_UNAVAILABLE;        //其他信息,運行期間的一些位標示
     long instance_size                                       OBJC2_UNAVAILABLE;        //類實例變量大小
     struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;        //該類的成員變量鏈表,是objc_ivar_list結構體指針
     struct objc_method_list * _Nullable * _Nullable methodLists  OBJC2_UNAVAILABLE;    //方法鏈表結構體
     struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;        //對象使用過的方法鏈表
     struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;        //協議鏈表
     #endif
 
 } OBJC2_UNAVAILABLE;
 /* Use `Class` instead of `struct objc_class *` */

其中!__OBJC2__表示非Objective-C 2.0的版本,Objective-C是從1.0發展到現在的2.0,這個聲明是爲了兼容1.0的Objective-C,若是Objective-C 2.0的話,可見objc_class與objc_object的定義是一樣的,都是隻有一個isa指針,所以說在Objective-C裏面,類也是一個對象

struct objc_ivar_list {
        int ivar_count                                           OBJC2_UNAVAILABLE;
        /* variable length structure */
        struct objc_ivar ivar_list[1]                            OBJC2_UNAVAILABLE;
}                                                                OBJC2_UNAVAILABLE;
//objc_ivar 變量結構體---名稱,類型,偏移字節和佔用的空間
struct objc_ivar {
    char * _Nullable ivar_name                               OBJC2_UNAVAILABLE;
    char * _Nullable ivar_type                               OBJC2_UNAVAILABLE;
    int ivar_offset                                          OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
}                                                            OBJC2_UNAVAILABLE;
struct objc_method_list {
    struct objc_method_list * _Nullable obsolete             OBJC2_UNAVAILABLE;

    int method_count                                         OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
    /* variable length structure */
    struct objc_method method_list[1]                        OBJC2_UNAVAILABLE;
}                                                            OBJC2_UNAVAILABLE;
//objc_method  //對象的每個方法的結構體,SEL是方法選擇器,是HASH後的值,可以通過這個值找到函數體的實現,IMP 是函數指針
struct objc_cache {
    unsigned int mask /* total = mask + 1 */                 OBJC2_UNAVAILABLE;
    unsigned int occupied                                    OBJC2_UNAVAILABLE;
    Method _Nullable buckets[1]                              OBJC2_UNAVAILABLE;
};
struct objc_protocol_list {
    struct objc_protocol_list * _Nullable next;
    long count;
    __unsafe_unretained Protocol * _Nullable list[1];
};


     實線是super_class 指針,虛線是isa指針。根元類的超類NSObject,而isa 指向了自己。NSObject的超類爲nil,也就是它沒有超類

     類的實例對象的isa 指向它的類,類的 isa 指向該類的 metaclass

     類的super_class 指向其父類,如果該類爲根類則值爲NULL

     metaclass的 isa 指向根 metaclass,如果該 metaclass 是根 metaclass 則指向自身。

     metaclass的 super_class 指向父 metaclass,如果該 metaclass 是根 metaclass 則指向該 metaclass 對應的類。

    Object-C爲每個類的定義生成兩個objc_class,一個普通的class,另一個即metaclass.可以在運行期創建這兩個objc_class數據結構,然後使用objc_addClass將 class 註冊到運行時系統中,以實現動態創建一個新類。

     class              實例對象的isa 指向的結構體

     metaclass     class 的 isa 指向的一個結構體

動態創建類和對象:

動態創建類:

    1、爲"classpair" 分配空間   創建一個新類和元類

//如果要創建一個根類,則superclass 指定爲Nil
//extraBytes:通常指定爲0,該參數是分配給類和元類對象尾部的索引ivars的字節數
objc_allocateClassPair(Class  superclass, const char *name, size_t extraBytes)

     2、爲創建的類添加方法和成員變量

//實例方法和實例變量應該提添加到類自身上,而類方法應該添加到類的元類上
//向類中添加一個方法 cls: 目標類  name:新方法  imp:函數的地址
class_addMethod(Class cls, SEL name, IMP  imp, const char *types)
//向類中添加一個實例變量
class_addIvar(Class cls, const char * name, size_t size, uint8_talignment, const char * types)

    3、註冊創建的類,使其可用

//註冊這個類
objc_registerClassPair(Class cls)
//這個例子是在運行時創建一個NSError 的子類RuntimeErrorSubclass,然後爲這個類添加一個方法reportFunction,這個方法的實現是ReportFunction
Class newClass = objc_allocateClassPair([NSError class], "RuntimeErrorSubclass", 0);
class_addMethod(newClass,@selector(reportFunction), (IMP)ReportFunction, "v@:");
objc_registerClassPair(newClass);
    
id c = [[newClass alloc] init];
[c performSelector:@selector(reportFunction) withObject:nil];
 
void ReportFunction(id self,SEL _cmd){
    NSLog(@"This object is%p",self);
    NSLog(@"Class is %@,andsuper is %@",[self class],[self superclass]);
    Class currentClass = [self class];
    for (int i = 0; i < 4; i ++) {
        NSLog(@"Following theisa pointer %d times gives %p",i,currentClass);
        currentClass = object_getClass(currentClass);
    }
    NSLog(@"NSObject's classis %p", [NSObjectclass]);
    NSLog(@"NSObject's metaclass is %p",object_getClass([NSObject class]));
}
    //運行後打印結果爲:
    [64427:18295107] This object is 0x6040004438a0
    [64427:18295107] Class is RuntimeErrorSubclass,andsuper is NSError
    [64427:18295107] Following the isa pointer 0 timesgives 0x60400025cb30
    [64427:18295107] Following the isa pointer 1 timesgives 0x60400025c2f0
    [64427:18295107] Following the isa pointer 2 timesgives 0x111511e58
    [64427:18295107] Following the isa pointer 3 timesgives 0x111511e58
    [64427:18295107] NSObject's class is 0x111511ea8
    [64427:18295107] NSObject's meta class is0x111511e58

    在for循環中,通過objc_getClass 來獲取對象的isa,並將其打印出來,依次可以回溯到NSObject meta-calss

    分析結果可以看到最後指針指向的地址是0x0,即NSObjectmeta-class的類地址。

動態創建對象:






1、動態爲一個類添加方法:

手動創建一個Dog類,在viewDidLoad方法中寫上以下代碼:

 //performSelector: 動態添加方法
    Dog *dog = [[Dog alloc] init];
    
//    [dog performSelector:@selector(eat)];  //不帶參數的方法
    [dog performSelector:@selector(eat:) withObject:@123];  //帶參數的方法
然後創建一個在Dog類的.m文件中進行動態添加方法:
#import "Dog.h"
#import <objc/message.h>

@implementation Dog

//定義一個函數
//默認一個方法都有兩個參數:self,_cmd 是隱式參數
//self: 方法的調用者  _cmd : 調用方法的編號 再增加參數直接往後添加例如:id param
void test(id self, SEL _cmd,id param){
    NSLog(@"調用了test %@ -- %@ -- %@ ",self,NSStringFromSelector(_cmd),param);  //不帶參數的打印結果:調用了test <Dog: 0x7f88e4836b70> -- eat
}
//帶有參數的打印結果: 調用了test <Dog: 0x7f8e8ad7a680> -- eat: -- 123

//動態添加方法,首先要實現resolveInstanceMethod
//resolveInstanceMethod調用: 當調用沒有實現的方法時就會調用resolveInstanceMethod這個方法
//resolveInstanceMethod作用: 知道哪些方法沒有實現,從而動態添加方法
//sel : 獲取到沒有實現的方法      NSLog(@"%@",NSStringFromSelector(sel)); // 打印結果:"eat"
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    
    //動態添加eat方法
    if (sel == @selector(eat:)) {
        /*
         __unsafe_unretained Class cls: 給哪個類添加方法
         SEL name : 添加方法的方法編號是什麼
         IMP imp:方法實現,函數的入口,函數名
         const char *types: 方法的類型
         */
        class_addMethod(self, sel, (IMP)test, "v@:@");  //v :返回值 void  @ : 參數id   :  :是SEL  
    }
    
    return [super resolveInstanceMethod:sel];
}

關於方法的類型的所有的情況如下:
/*
 c<span style="white-space:pre">		</span>A char
 i<span style="white-space:pre">		</span>An int
 s<span style="white-space:pre">		</span>A short
 l<span style="white-space:pre">		</span>A long
            <span style="white-space:pre">	</span>l is treated as a 32-bit quantity on 64-bit programs.
 q<span style="white-space:pre">		</span>A long long
 C<span style="white-space:pre">		</span>An unsigned char
 I<span style="white-space:pre">		</span>An unsigned int
 S<span style="white-space:pre">		</span>An unsigned short
 L<span style="white-space:pre">		</span>An unsigned long
 Q<span style="white-space:pre">		</span>An unsigned long long
 f<span style="white-space:pre">		</span>A float
 d<span style="white-space:pre">		</span>A double
 B<span style="white-space:pre">		</span>A C++ bool or a C99 _Bool
 v<span style="white-space:pre">		</span>A void
 *<span style="white-space:pre">		</span>A character string (char *)
 @<span style="white-space:pre">		</span>An object (whether statically typed or typed id)
 #<span style="white-space:pre">		</span>A class object (Class)
 :<span style="white-space:pre">		</span>A method selector (SEL)
 [array type]<span style="white-space:pre">	</span>An array
 {name=type...}<span style="white-space:pre">	</span>A structure
 (name=type...)<span style="white-space:pre">	</span>A union
 bnum<span style="white-space:pre">		</span>A bit field of num bits
 ^type<span style="white-space:pre">		</span>A pointer to type
 ?<span style="white-space:pre">		</span>An unknown type (among other things, this code is used for function pointers)
 */

2、分類添加屬性 

嘗試給NSObject 添加一個屬性,在ViewDidLoad中調用:

    NSObject *objc = [[NSObject alloc] init];
    objc.name = @"hehe";
    NSLog(@" -- %@",objc.name);  //打印結果: -- hehe
爲NSObject添加一個分類如下:
@interface NSObject (Objc)
@property (nonatomic,copy) NSString *name;
@end
#import "NSObject+Objc.h"
#import <objc/message.h>

@implementation NSObject (Objc)

-  (void)setName:(NSString *)name {
    //添加屬性,一般都是相對於對象而言的,給某一個對象添加屬性就是給某個對象產生關聯
    /*
     id object : 給哪個對象添加屬性
     const void *key : 屬性名,根據key去獲取關聯的對象
     id value :關聯的值
     objc_AssociationPolicy policy : 策略
     */
    objc_setAssociatedObject(self, @"name", name,  OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSString *)name {
    return objc_getAssociatedObject(self, @"name");
}

 _cmd:

    在OC 中表示當前selector,正如self表示當前方法調用的實例對象

    NSLog(@"%@",NSStringFromSelector(_cmd));  //打印當前調用的方法名

發佈了84 篇原創文章 · 獲贊 23 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章