1.objc_object 与 对象的关系
所有的OC对象都是以objc_object为模板继承过来的
所有的对象都是NSObject(OC)子类,但底层的是一个objc_object(C/C++)的结构体类型
总结:objc_object与对象是继承关系
2.什么是属性、成员变量、实例变量?
2.1)属性(property):在OC中是通过@property定义,实质带下划线成员变量+setter+ getter方法的变量组合
注意:定义了属性,默认是@synthesize修饰,此时,你不能同时重写这个属性的setter、getter方法。原因是:自己定义了setter、getter方法,相当于是自己管理这个属性,且属性要用@dynamic <#property#>
2.2)成员变量(ivar):在OC的类中{}中定义的,且没有下划线的变量
2.3)实例变量:通过当前对象类型实例化的变量,是一种特殊的成员变量
3.元类中为什么会有类方法?
在中的探索中,我们知道了实例方法存储在类中,类方法存储在元类中。为了探索这个问题,定义了以下几个方法,来探索方法的归属问题:
在Person中定义一个实例方法和一个类方法
@interface Person : NSObject
//赚钱
- (void)earnMoney;
//花钱
+ (void)spendMoney;
@end
@implementation Person
- (void)earnMoney{
NSLog(@"Person earn momey!!!");
}
+ (void)spendMoney{
NSLog(@"Person spend money!!!");
}
@end
main 主函数,用于调用自定义的方法
int main(int argc, char * argv[]) {
@autoreleasepool {
Person *person = [Person alloc];
Class pClass = object_getClass(person);
cwObjc_copyMethodList(pClass);
cwInstanceMethod_classToMetaclass(pClass);
cwClassMethod_classToMetaclass(pClass);
cwIMP_classToMetaclass(pClass);
NSLog(@"Hello, World!");
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
cwObjc_copyMethodList 函数:用于获取类的方法列表
void cwObjc_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);
}
cwInstanceMethod_classToMetaclass 函数:用于获取类的实例方法
void cwInstanceMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getInstanceMethod(pClass, @selector(earnMoney));
Method method2 = class_getInstanceMethod(metaClass, @selector(earnMoney));
Method method3 = class_getInstanceMethod(pClass, @selector(spendMoney));
Method method4 = class_getInstanceMethod(metaClass, @selector(spendMoney));
NSLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
cwClassMethod_classToMetaclass 函数:用于获取类的类方法
void cwClassMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getClassMethod(pClass, @selector(earnMoney));
Method method2 = class_getClassMethod(metaClass, @selector(earnMoney));
Method method3 = class_getClassMethod(pClass, @selector(spendMoney));
Method method4 = class_getClassMethod(metaClass, @selector(spendMoney));
NSLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
cwIMP_classToMetaclass 函数:用于获取方法的实现
void cwIMP_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
// - (void)earnMoney;
// + (void)spendMoney;
IMP imp1 = class_getMethodImplementation(pClass, @selector(earnMoney));
IMP imp2 = class_getMethodImplementation(metaClass, @selector(earnMoney));
IMP imp3 = class_getMethodImplementation(pClass, @selector(spendMoney));
IMP imp4 = class_getMethodImplementation(metaClass, @selector(spendMoney));
NSLog(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);
NSLog(@"%s",__func__);
}
以下是几个函数调用的打印结果:
3.1)首先分析class_copyMethodList,从官方文档中可以得知,他是获取类实例方法,且不包含父类的实例方法。类没有实例方法或为nil,返回NULL。所以,只有earnMoney。
3.2)cwInstanceMethod_classToMetaclass是通过class_getInstanceMethod获取实例方法,我们看看源码:
Method class_getInstanceMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
// This deliberately avoids +initialize because it historically did so.
// This implementation is a bit weird because it's the only place that
// wants a Method instead of an IMP.
#warning fixme build and search caches
// Search method lists, try method resolver, etc.
lookUpImpOrForward(nil, sel, cls, LOOKUP_RESOLVER);
#warning fixme build and search caches
return _class_getMethod(cls, sel);
}
⬇️
static Method _class_getMethod(Class cls, SEL sel)
{
mutex_locker_t lock(runtimeLock);
return getMethod_nolock(cls, sel);
}
⬇️
static method_t *
getMethod_nolock(Class cls, SEL sel)
{
method_t *m = nil;
runtimeLock.assertLocked();
// fixme nil cls?
// fixme nil sel?
ASSERT(cls->isRealized());
while (cls && ((m = getMethodNoSuper_nolock(cls, sel))) == nil) {
cls = cls->superclass;
}
return m;
}
⬇️
static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
runtimeLock.assertLocked();
ASSERT(cls->isRealized());
// fixme nil cls?
// fixme nil sel?
auto const methods = cls->data()->methods();
for (auto mlists = methods.beginLists(),
end = methods.endLists();
mlists != end;
++mlists)
{
// getMethodNoSuper_nolock is the hottest
// caller of search_method_list, inlining it turns
// getMethodNoSuper_nolock into a frame-less function and eliminates
// any store from this codepath.
method_t *m = search_method_list_inline(*mlists, sel);
if (m) return m;
}
return nil;
}
从源码中可以看到,根据传入的Class与SEL查找到Method。earnMoney是存储在类中的,元类中没有;spendMoney是存储在元类中的,元类中没有。
注意:
1.明明是去查找方法,为什么要调用lookUpImpOrForward?
lookUpImpOrForward是查找方法的实现,但是,在这个方法中,会检测CFI攻击及保证类进行了初始化。
2.CFI:控制流完整性(Control Flow Integrity, CFI)是由加州大学和微软公司于2005年提出的一种防御控制流劫持攻击的安全机制。
3.3)cwClassMethod_classToMetaclass是通过class_getClassMethod获取类方法的,我们可以看看源码
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
⬇️
Class getMeta() {
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
哎哟,类方法查找本质是查找元类的实例方法。在获取元类中,如果是元类,直接返回,不是元类会去获取元类。class_getClassMethod中的cls不管传入是类还是元类,只要sel是类方法且存在,就能找到改方法
。
3.4)cwIMP_classToMetaclass是通过class_getMethodImplementation函数获取imp的,获取结果:
class_getMethodImplementation的源码:
IMP class_getMethodImplementation(Class cls, SEL sel)
{
IMP imp;
if (!cls || !sel) return nil;
imp = lookUpImpOrNil(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);
// Translate forwarding function to C-callable external version
if (!imp) {
return _objc_msgForward;
}
return imp;
}
可以看到,他返回的IMP有两种,一种是方法的实现,一种是消息转发的函数指针。如果SEL没有实现,会返回消息转发函数指针,函数地址是一样的。所以,在类可以找到earnMoney方法实现,元类中可以找到spendMoney方法实现
4.iskindOfClass&isMemberOfClass的理解
iskindOfClass & isMemberOfClass 类方法调用
void isKindOrMember(){
//-----使用 iskindOfClass & isMemberOfClass 类方法
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];//1
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];//0
BOOL re3 = [(id)[Person class] isKindOfClass:[Person class]];//0
BOOL re4 = [(id)[Person class] isMemberOfClass:[Person class]];//0
NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);
//iskindOfClass & isMemberOfClass 实例方法调用
//------iskindOfClass & isMemberOfClass 实例方法
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];//1
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];//1
BOOL re7 = [(id)[Person alloc] isKindOfClass:[Person class]];// 1
BOOL re8 = [(id)[Person alloc] isMemberOfClass:[Person class]];//1
NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
}
其最终结果打印如下:
我们看源码:
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
断点的时候,发现isKindOfClass、class不会被调用。why?一想肯定是llvm这个坏人干的好事儿,去一查,果然是他,发现他悄悄地将isKindOfClass换成了objc_opt_isKindOfClass,class换成objc_opt_class,所以,最总走的源码应该是:
// Calls [obj isKindOfClass]
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
if (slowpath(!obj)) return NO;
Class cls = obj->getIsa();
if (fastpath(!cls->hasCustomCore())) {
for (Class tcls = cls; tcls; tcls = tcls->superclass) {
if (tcls == otherClass) return YES;
}
return NO;
}
#endif
return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
// Calls [obj class]
Class
objc_opt_class(id obj)
{
#if __OBJC2__
if (slowpath(!obj)) return nil;
Class cls = obj->getIsa();
if (fastpath(!cls->hasCustomCore())) {
return cls->isMetaClass() ? obj : cls;
}
#endif
return ((Class(*)(id, SEL))objc_msgSend)(obj, @selector(class));
}
总结:
1.objc_opt_class很明显,如果传入的是实例对象,返回的是类对象。否则,传入什么返回什么(传入类对象返回该类对象,传入元类返回该元类)。
2.objc_opt_isKindOfClass方法是先获取传入对象的isa(类/元类),从isa开始查找,然后,从isa的继承链上查找。
3.+isMemberOfClass是取isa与传入对象比较
4.-isMemberOfClass是取类与传入对象比较
接下来,逐一解释:
4.1)BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];//1
由objc_opt_clas
s可以得知[NSObject class]
返回的是类对象NSObject
。由objc_opt_isKindOfClass
可以的得知,将从元类NSObject
开始遍历,并与类NSObject
比较。由isa的走位可以知道元类NSObjec
t的superclass
是类NSObject
,所以,NSObject元类.superclass == NSOject
为true。
4.2)BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];//0
由objc_opt_clas
s可以得知[NSObject class]
返回的是类对象NSObject
。由isMemberOfClass
类方法可以的得知,将从类NSObject
取出isa,即元类NSObject
与类NSObject
比较。显然,这两个NSObject是不等的,一个是类,一个是元类。
4.3)BOOL re3 = [(id)[Person class] isKindOfClass:[Person class]];//0
由objc_opt_clas
s可以得知[Person class]
返回的是类对象Person
。由objc_opt_isKindOfClass
可以的得知,将从元类Person
开始遍历,并与类Person
比较。由isa的走位可以知道:元类Person
的superclass
是元类NSObject
。所以,元类Person
->元类NSObject
->类NSObject
->nil中没有一个和类Person
相同。
4.4)BOOL re4 = [(id)[Person class] isMemberOfClass:[Person class]];//0
由objc_opt_clas
s可以得知[Person class]
返回的是类对象Person
。由isMemberOfClass
可以的得知,将从类Person
取出isa,即元类Person
与类Person
比较。显然,这两个Person是不等的,一个是类,一个是元类。
4.5)BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];//1
由objc_opt_isKindOfClass
可以的得知,将从实例NSObject
中获取类NSObject
,从类NSObject
开始遍历,并与类NSObject
比较。所以,类NSObject
== 类NSObject
为true。
4.6)BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];//1
由isMemberOfClass
实例方法可以的得知,将从实例NSObject
取出class,由objc_opt_class
可以得知[NSObject class]
是类NSObject
。即类NSObject
与类NSObject
比较。显然,这两个NSObject是等的。
4.7)BOOL re7 = [(id)[Person alloc] isKindOfClass:[Person class]];// 1
由objc_opt_isKindOfClass
可以的得知,将从实例Person
中获取类Person
,从类Person
开始遍历,并与类Person
比较。所以,所以,类Person
== 类Person
为true。
4.8)BOOL re8 = [(id)[Person alloc] isMemberOfClass:[Person class]];//1
由isMemberOfClass
实例方法可以的得知,将从实例Person
取出class,由objc_opt_class
可以得知[person class]
是类Person
,即类Person
与类Person
比较。显然,这两个Person是等的。