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,即NSObject的meta-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)); //打印當前調用的方法名