OC對象 主要分三種:
instance 對象(實例對象)
class 對象(類對象)
meta-class 對象 (元類對象)
1:一個實例對象 在內存中存儲的信息:isa指針、其他變量(這個是成員變量的值,比如說,self.age = 10, 10放在裏面)
2:類對象
NSObject *objec1 = [[NSObject alloc] init];
NSObject *objec2 = [[NSObject alloc] init];
// 獲取類對象的三種方式
Class objectClass1 = [objec1 class];
Class objectCalss2 = [objec2 class];
Class objectClass3 = object_getClass(objec1);
Class objectClass4 = object_getClass(objec2);
Class objectClass5 = [NSObject class];
一個類的類對象是唯一的,在內存中是一份的,所以上述objectClass1、2、3、4、5地址是一樣的
2018-07-04 20:39:35.659080+0800 new[4539:831047] 0x7fffb08b9140,0x7fffb08b9140,0x7fffb08b9140,0x7fffb08b9140,0x7fffb08b9140
類對象在內存中存儲的信息:isa指針、superclass指針、類的屬性信息(@property)、類的對象方法信息(instance method)(減號的方法)、類的協議信息(protocol)、類的成員變量信息(ivar)(這個成員變量是 成員變量的名字、還有成員變量的類型,比方說 string類型,名字叫age)
3:meta-class 元類對象
獲取元類對象
跟class和實例對象不是一種地址
class方法返回的就是類對象,不管你調用多少次,[[NSObject class] class]
元類對象在內存中儲存的信息:類方法。
objectMetaClass是NSObject的meta-Class對象
每個類在內存中有且只有一個metal-Class對象
meta-Class對象和class對象的內存結構是一樣的,但是用途不一樣,在內存中存儲的信息主要包括:isa指針、superclass、類方法信息。
可以判斷是否是元類對象。:
class_isMetaClass(objectMetaClass);
object_getClass與objc_getClass分別是什麼意思
A>: Class objc_getClass(const char *name)
a:傳入的是字符串類名,返回對應的類對象。
B>: Class object_getClass(id obj)
a:傳進去的obj可能是instance對象、class對象、meta-class對象
b:如果是instance對象,返回class對象
c:如果是class對象,返回的是meta-class對象
d:如果是meta-class對象,返回的是NSObject(基類)的meta-class對象。
C>: -(Class)class 、+(Class)class
返回的就是類對象
isa指針:
class對象的superclass指針:Student繼承自Person,Person繼承自NSObject。當Student實例變量去調用實例方法時的過程。
@interface Person : NSObject
{
int _df;
}
- (void)personMethod;
@end
@implementation Person
- (void)personMethod
{
}
@end
@interface Student : Person
{
double _no;
}
- (void)studentMethod;
@end
@implementation Student
- (void)studentMethod
{
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Student *stu = [[Student alloc] init];
[stu personMethod];
}
return NSApplicationMain(argc, argv);
}
這個原理過程就是下面圖中所示,stu先按照上圖所示的,stu的實例對象的isa指針,找到本身stu類(因爲類裏有對象方法,同時有superclass指針,)發現並未有該方法,根據superclass指針,找到Person的類對象,就能夠找到Person類的對象方法,正好有剛調用的personMehod方法,即可實現。
假設Student類對象調用 load方法,那麼過程就是下面圖中所示:Student類根據isa指針找到自己的meta-class對象,在元類對象中並未發現load方法,就會根據superclass找到父類的meta-class對象,也就是Person的元類對象,發現也沒有,再根據person原類對象的superclass找到父類也就是NSObject的meta-class對象,從而找到load方法。這裏需要注意,假設NSObject沒有load方法,那麼現在NSObject這個基類的meta-class的superclass會找到NSObject的class,如果再沒找到,纔會報錯。
總結一下:就是superclass找的是類方法 都是找的meta-class對象 如果找的是實例方法 ,那麼找的都是類對象。
每一個類 都有自己的元類對象
假設Person和Student都有實例test方法。【student test】調用的時候,會直接調用student的test方法。原因:面向對象的思想的話是子類重寫父類,那麼肯定會調用子類的方法。如果按照上面的本質去解釋的話:子類isa指針找到本類class對象,發現有test方法,那麼它就直接執行了,不會再找父類了。
如果是
驗證問題:
這個是NSObject分類,只實現了實例方法test。
#import "NSObject+test.h"
@implementation NSObject (test)
- (void)test
{
NSLog(@"sfd");
}
@end
這裏是person方法,並未實現test類方法
@interface Person : NSObject
+ (void)test;
@end
@implementation Person
@end
#import "NSObject+test.h"
int main(int argc, const char * argv[]) { @autoreleasepool {
[Person test];
}
return NSApplicationMain(argc, argv);
}
那麼這個能夠調用? 是否會報錯? 答案很明顯,不會
2018-07-10 20:08:51.314998+0800 new[1765:218776] sfd
原因就是上面基類的meta-class方法如果沒有找到對應的類方法的時候,它的superclass指針會指向基類的class對象,也就是NSObject類,找到了test方法,所以就調用了。對於本質objc_Send()方法而言,是沒有減號和加號的區別方法的,所以直接調用。
注意:
isa不是直接指向本類的對象的,從64bit開始,需要一次運算才能計算出真實的地址
但是superclass是直接指向的,並不需要轉換
2:對象的isa指針指向哪裏?
instance對象isa指向class對象
class對象isa指向meta-class對象
meta-class對象isa指向基類meta-class對象
3:OC的類信息存放在哪裏?
對象方法、屬性、成員變量、協議信息存放在class對象中
類方法存在meta-class對象中
成員變量的具體值,存放在instance對象