咱們以前分析了對象,那麼今天來分析一下建立對象的 類~緩存
探索環境:libObjc - 779.1
Person(自定義類)中聲明一個成員變量、一個屬性、一個方法、一個類方法markdown
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface Person : NSObject{
NSString *hobby;
}
@property (nonatomic, copy) NSString *nickName;
- (void)sayHello;
+ (void)sayHappy;
@end
@implementation Person
- (void)sayHello{
NSLog(@"LGPerson say : Hello!!!");
}
+ (void)sayHappy{
NSLog(@"LGPerson say : Happy!!!");
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [Person alloc];
//拿到person這個對象的類
Class pClass = object_getClass(person);
NSLog(@"%@ - %p",person,pClass);
}
return 0;
}
複製代碼
在oc中,全部的類都是能夠用Class
來接收的。app
Class
繼承自objc_class
clang
一下,看一下底層編譯,Class
是繼承自哪裏的:函數
typedef struct objc_class *Class;
複製代碼
objc_class
繼承自objc_object
繼續看一下objc_class
:post
struct objc_class {
Class _Nonnull isa __attribute__((deprecated));
} __attribute__((unavailable));
複製代碼
這個方法好像被棄用了,那咱們去源碼libObjc
中找一下objc_class
,源碼裏有幾個關於它的方法,大部分都被棄用了,咱們找到了一個可用的:優化
struct objc_class : objc_object {
// Class ISA; //這裏是一個隱藏屬性
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
···下面是這個結構體的一些方法和函數,暫時用不到,省略掉···
}
複製代碼
咱們看到,這裏面有一個隱藏的isa,那它必然是繼承自父類的一個屬性,咱們繼續看一下objc_object
來驗證一下:ui
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
複製代碼
Class ISA
咱們以前的探索:isa
是isa_t
類型,爲何在objc_object
中的isa
是Class類型
呢?this
一、萬物皆對象,isa是能夠用Class來接收的
二、在早期,isa的就是用來返回一個類,後來優化爲返回nonpointer和純淨的isa,這裏多是延續了一個習慣 三、在ISA()
方法(分析isa的文章中有提到)中能夠看到,返回值是一個(Class)
強轉類型atom
NSObject
與objc_object
的關係 (對象)咱們知道在oc中,萬物皆對象!其實objc_object
就是一切的鼻祖! NSObject
算是objc_object
的一個仿寫、衍生類,結構按理來講應該和objc_object
是如出一轍的,驗證一下:
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
複製代碼
也能夠這麼說,objc_object
是c,NSObject
是oc,NSObject
是對objc_object
的封裝,到底層編譯仍是會變成objc_object
。
NSObject
(對象)的本質是objc_object
NSObject
與objc_class
的關係 (類)與objc_class
對應的,實際上是NSObject Class
,是NSObject
這個類。底層編譯就會變成objc_class
NSObject
(類)的本質是objc_class
從上面的分析,能夠看出:類是一個結構體,裏面存放了isa、superClass、cache、bits等。
isa指針
,不只實例對象中有,類對象中也有。佔8字節
superclass
父類,class* 自己也是一個指針。佔8字節
cache
緩存,追蹤進去看一下cache_t
結構體的類型,而不是結構體指針類型(佔8位),就須要計算一下了:
struct cache_t {
struct bucket_t *_buckets; // 結構體*類型=對象,8
mask_t _mask; // int32位類型,4
mask_t _occupied; // int32位類型,4
···一些方法函數,不佔內存,省略···
}
typedef uint32_t mask_t;
複製代碼
cache_t
的類型,佔16位
bits
實際上是用來存儲數據的,咱們在前面計算內存大小,是爲了在內存中偏移,直接拿到bits
,來驗證裏面是否存放了咱們在最前面準備工做中的屬性、方法。
在objc_class
中,前面省略了一個方法data()
,這個是class_rw_t
類型的結構體
struct objc_class : objc_object {
//···省略前面
class_data_bits_t bits;
class_rw_t *data() {
return bits.data();
}
//···省略後面
}
複製代碼
class_rw_t
:
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
//···省略後面
}
複製代碼
偷偷開個上帝視角,看一下ro
:
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
method_list_t *baseMethods() const {
return baseMethodList;
}
};
複製代碼
咱們能夠看出,這裏面實際上是經過bits
來存儲咱們的methods
、properties
、protocols
等等信息的。(ro
中也有相關信息的屬性)咱們稍後來驗證一下~
咱們在第三部分,計算出從首地址偏移32位就能拿到bits
。32對應的16進制是0x20,而後(class_data_bits_t *)
強轉,咱們直接LLDB來操做一番(每次從新運行,內存地址可能就跟上一次不同了):
咱們看到bits
裏面有properties!
咱們聲明的屬性和成員變量是否是存在這裏呢?打印一下:
(property_array_t 實際上是一個二維數組的類型。咱們繼續打印裏面的信息)
的確有!可是,有沒有發現這個裏面的信息並不全,由於直接在bits
裏沒有找到咱們聲明的成員變量的地方。
開啓上帝視角:咱們直接讀ro
!咱們來看一下:
屬性 - baseProperties
:
成員變量 - ivars
:
果真,在這裏面都找到了!
繼續看ro
下的baseMethodList
哇!list有4個,分別是:類的實例方法、屬性幫咱們生成的setter、getter方法,還有C++的方法。
可是,咱們聲明的 類方法 在哪裏呢?
咱們開啓上帝視角:類方法在元類中,咱們去驗證一下:
類的類方法,的確在這裏!
以前說過,萬物皆對象,類也是個對象,對象的實例方法在類中;
那類方法能夠理解爲 類對象的實例方法,就在元類中。
這裏瞭解便可,直接把源碼貼出來,有興趣的小夥伴能夠自行運行:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface Person : NSObject{
NSString *hobby;
}
@property (nonatomic, copy) NSString *nickName;
- (void)sayHello;
+ (void)sayHappy;
@end
@implementation Person
- (void)sayHello{
NSLog(@"LGPerson say : Hello!!!");
}
+ (void)sayHappy{
NSLog(@"LGPerson say : Happy!!!");
}
@end
void testObjc_copyIvar_copyProperies(Class pClass){
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(pClass, &count);
for (unsigned int i=0; i < count; i++) {
Ivar const ivar = ivars[i];
//獲取實例變量名
const char*cName = ivar_getName(ivar);
NSString *ivarName = [NSString stringWithUTF8String:cName];
NSLog(@"class_copyIvarList:%@",ivarName);
}
free(ivars);
unsigned int pCount = 0;
objc_property_t *properties = class_copyPropertyList(pClass, &pCount);
for (unsigned int i=0; i < pCount; i++) {
objc_property_t const property = properties[i];
//獲取屬性名
NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
//獲取屬性值
NSLog(@"class_copyProperiesList:%@",propertyName);
}
free(properties);
}
void testObjc_copyMethodList(Class pClass){
unsigned int count = 0;
Method *methods = class_copyMethodList(pClass, &count);
for (unsigned int i=0; i < count; i++) {
Method const method = methods[i];
//獲取方法名
NSString *key = NSStringFromSelector(method_getName(method));
NSLog(@"Method, name: %@", key);
}
free(methods);
}
void testInstanceMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));
Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
NSLog(@"%p-%p-%p-%p",method1,method2,method3,method4);
NSLog(@"%s",__func__);
}
void testClassMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getClassMethod(pClass, @selector(sayHello));
Method method2 = class_getClassMethod(metaClass, @selector(sayHello));
Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy)); // ?
NSLog(@"%p-%p-%p-%p",method1,method2,method3,method4);
NSLog(@"%s",__func__);
}
void testIMP_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));
IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy));
IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));
NSLog(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);
NSLog(@"%s",__func__);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [Person alloc];
Class pClass = object_getClass(person);
testObjc_copyIvar_copyProperies(pClass);
testObjc_copyMethodList(pClass);
testInstanceMethod_classToMetaclass(pClass);
testClassMethod_classToMetaclass(pClass);
NSLog(@"Hello, World!");
}
return 0;
}
複製代碼
今天經過LLDB一步步調試,雖然有點繁瑣,但也驗證了許多隻知其一不知其二的地方:
類的本質是個結構體(萬對皆對象,類的本質是個對象,對象的本質是個結構體)
類的結構裏包含了 isa、superClass、cache、bits
Class
是一個objc_class
類型
objc_class
繼承自objc_object
類的屬性、成員變量、方法、協議等 存儲在class_ro_t
這個結構體裏
類的類方法,存在元類的class_ro_t
中
類和元類的建立時機是在編譯時(探索isa時,在最後驗證過)