nonpointer
0,代表普通的指针,存储着Class、Meta-Class对象的内存地址
1,代表优化过,使用位域存储更多的信息has_assoc
是否有设置过关联对象,如果没有,释放时会更快has_cxx_dtor
是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快shiftcls
存储着Class、Meta-Class对象的内存地址信息(通过ias地址与ISA_MASK(0x0000000ffffffff8ULL)
按位与就可以得到class对象的地址,可以看出对象的地址总是0或8,因为后面三位不是对象的指向对象的,所以参与运算以后都是0)magic
用于在调试时分辨对象是否未完成初始化weakly_referenced
是否有被弱引用指向过,如果没有,释放时会更快deallocating
对象是否正在释放extra_rc
里面存储的值是引用计数器减1has_sidetable_rc
引用计数器是否过大无法存储在isa中
如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
class_rw_
t里面的methods、properties、protocols是二维数组,是可读可写的,包含了类的初始内容、分类的内容。其方法列表具体结构示意图如下:class_ro_t
里面的baseMethodList、baseProtocols、ivars、baseProperties是一维数组,是只读的,包含了类的初始内容。
头文件中可以看出的 typedef struct objc_method *Method;
,Method
方法的本质是objc_method
结构体,runtime原文件可以找到objc_method的底层结构如下:struct objc_method {
SEL name; //函数名
const char * types ;//编码(返回值类型,参数类型)
IMP mp ;//指向函数的指针
} ;
SEL
代表方法\函数名,一般叫做选择器,底层结构跟char *类似。
可以通过@selector()
和sel_registerName()
获得;
可以通过sel_getName()
和NSStringFromSelector()
转成字符串;
不同类中相同名字的方法,所对应的方法选择器是相同的。
SEL底层结构:
typedef struct objc_selector *SEL;
types
包含了函数返回值、参数编码的字符串。
IMP
代表函数的具体实现。
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...);
NSLog(@"%s",@encode(int));
NSLog(@"%s",@encode(char));
NSLog(@"%s",@encode(NSObject));
NSLog(@"%s",@encode(NSArray));
struct cache_t {
bucket_t *_buckets;
mask_t _mask;
mask_t _occupied;
IMP imp(SEL selector)
{
mask_t begin = _mask & (long long)selector;//获得脚标,以便快速查找到对应的方法
mask_t i = begin;
do {
if (_buckets[i]._key == 0 || _buckets[i]._key == (long long)selector) {
return _buckets[i]._imp;
}
} while ((i = cache_next(i, _mask)) != begin);//如果没有找到对应的方法,就去查找比当前小的那一位有没有,依次遍历查找直到为0时就从最大的那一位再重新遍历,如果都没有就会报方法找不到。
return NULL;
}
};
struct bucket_t {
cache_key_t _key;
IMP _imp;
};
buckets
:是一个散列表(百度百科:哈希表),相当于一个数组里面存储很对字典,每个字典的key是SEL,即方法名_imp是指向行数的指针,用来调用函数。
_mask
:山列表的长度-1。
_occupied
:已经缓存的方法数量。
cache_t
里面有没有,有就从缓存中去,没有就将方法加入到cache_t
中。cache_t
时会先看散列表内存够不够,如果够就加进去,不够就重新分配内存(是之前内存的2倍),之前加入到散列表中的方法也就都没了。(比如第一次调用了方法a,第二次调用b的时候放学内存不够了,就重新分配内存,并且把b保存进去,这时候散列表并没有方法a了)。_mask & (long long)selector
(散列表长度减去1和selector按位与)计算获得脚标,通过这个脚标去散列表中快速定位方法。但是这样计算可能得到多个方法的脚标是一样的,这是就还要判断散列表里面的key是不是与当前方法名相同, 如果定查找到key和当前调用的方法名相同就去获取指向该方法的指针去调用该方法,如果没有找到就去比当前脚标小1位的去匹配,依次类推,如果到脚标到最小都没匹配到该方法,就从散列表最大的脚标从大到小重新遍历查找(但是大于最开始计算出来的脚标),找到了就调用,找不到就不再报错了。也就是最多遍历了一遍散列表,最少就一次就定位到了正确的方法。注意:
方法保存到方法缓存散列表中时也是上面的机制,如计算脚标对应的元素为空就将该方法的方法名和指向方法指针插入进去,如果不为空就用计算脚标减一去查看还有没空的地方,如果都没有了就重新分配跟大的内存去保存该方法。散列表是空间换时间(浪费更多的内存来换取快去查找方法)来达到快速存储和调用方法信息的。objc-msg-arm64.s
ENTRY _objc_msgSend
b.le LNilOrTagged
CacheLookup NORMAL
.macro CacheLookup
.macro CheckMiss
STATIC_ENTRY __objc_msgSend_uncached
.macro MethodTableLookup
__class_lookupMethodAndLoadCache3
STATIC_ENTRY __objc_msgForward_impcache
ENTRY __objc_msgForward
Core Foundation
forwarding(不开源)
objc-runtime-new.mm
_class_lookupMethodAndLoadCache3
lookUpImpOrForward
getMethodNoSuper_nolock、search_method_list、log_and_fill_cache
cache_getImp、log_and_fill_cache、getMethodNoSuper_nolock、log_and_fill_cache
_class_resolveInstanceMethod
_objc_msgForward_impcache
objc_msgSend
函数的调用,objc_msgSend
具体实现是汇编语言这里不再详解,在里面可以看到其主要是调用了lookUpImpOrForward
(objc-runtime-new.mm文件可以看起具体实现)。void objc_msgSend(id receiver, SEL selector)
{
if (receiver == nil) return;
//调用方法
}
objc_msgSend有两个参数receiver(消息接收者)
和selector(消息名称)
,也就是说每个方法都会有这两个参数,只不过是隐式参数不显示而已。
Person * person = [[Person alloc]init];
//对象方法
[person test];
//其编译后为:
objc_msgSend(person, sel_registerName("test"));
//等价于:
objc_msgSend(person,@selector(test));
//类方法
[Person initialize];
objc_msgSend((id)objc_getClass("Person"), sel_registerName("initialize"));
//等价于:
objc_msgSend([MJPerson class], @selector(initialize));
*objc_msgSend
的执行流程可以分为3大阶段:消息发送 、动态方法解析、消息转发。
class_rw_t
中查找方法,已经排序的,二分查找;没有排序的,遍历查找。IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
//isa,superclass机制都完成后还没找到方法的实现
//将要动态解析方法,并且还没有动态解析该方法
if (resolver && !triedResolver) {
runtimeLock.unlockRead();
//动态解析该方法
_class_resolveMethod(cls, sel, inst);
runtimeLock.read();
//标记已经解析了
triedResolver = YES;
//继续重复上面的isa和superclass机制
goto retry;
}
}
//动态解析方法
void _class_resolveMethod(Class cls, SEL sel, id inst)
{
//如果不是原类,就解析实例对象方法
if (! cls->isMetaClass()) {
// try [cls resolveInstanceMethod:sel]
_class_resolveInstanceMethod(cls, sel, inst);
}
else {
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
//否则就是解析类方法
_class_resolveClassMethod(cls, sel, inst);
if (!lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
_class_resolveInstanceMethod(cls, sel, inst);
}
}
}
//动态解析对象方法(本质还是objc_msgSend)
static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst)
{
if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
// Resolver not implemented.
return;
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(cls, SEL_resolveInstanceMethod, sel);
IMP imp = lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/);
if (resolved && PrintResolving) {
if (imp) {
_objc_inform("RESOLVE: method %c[%s %s] "
"dynamically resolved to %p",
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel), imp);
}
else {
// Method resolver didn't add anything?
_objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
", but no new implementation of %c[%s %s] was found",
cls->nameForLogging(), sel_getName(sel),
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel));
}
}
}
+(BOOL)resolveInstanceMethod:(SEL)sel
方法解析实例对象方法,如果是就调用+ (BOOL)resolveClassMethod:(SEL)sel
来动态解析类方法。#import
@interface Person : NSObject
-(void)test;
+(int)classTest:(int)a;
@end
#import "Person.h"
#import
@implementation Person
//运行时可以动态添加方法的实现,
- (void)other{
NSLog(@"%s",__func__);
}
//c语言函数函数实现
void other_c(id self,SEL _cmd){
NSLog(@"c_other - %@ - %@", self, NSStringFromSelector(_cmd));
}
//动态解析实例对象方法
+(BOOL)resolveInstanceMethod:(SEL)sel{
//如是方法名为test就动态添加方法的实现
if (sel == @selector(test)) {
//获取其他方法实现,两个参数分别表示是那个对象的那个对象方法
// Method method = class_getInstanceMethod(self, @selector(other));
//添加方法,4个参数表示:拥有该方法的类对象,方法名,指向方法实现的指针,方法的返回值和参数的enconde编码
// class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));
//如果是c语言函数直接添加
//c语言函数名就是指向c语言函数实现的指针
/*enconde编码参数分表示:
v返回值类型为void,
16 所有参数一共所占内存大小,
@ 第一个参数类型(self),
0 第一个参数从0开始的,
: 第二个参数方法名(SEL),
8 第二个参数从8开始的
*/
class_addMethod(self, sel, (IMP)other_c, "v16@0:8");
return YES;// 返回YES代表有动态添加方法,写NO也是可以的,只做参数打印调用
}
return [super resolveInstanceMethod:sel];
}
//类方法
+ (int)classOther:(int)a{
NSLog(@"%s",__func__);
return a++;
}
int classOther_c(id self,SEL _cmd,int a){
NSLog(@"%s:a=%d",__func__,a);
return a++;
}
//动态解析类方法
+ (BOOL)resolveClassMethod:(SEL)sel{
if (sel == @selector(classTest:)) {
//oc方法实现
// Method method = class_getClassMethod(self, @selector(classOther:));
// class_addMethod(object_getClass(self), sel, method_getImplementation(method), method_getTypeEncoding(method));
//c语言方法实现
// class_addMethod(object_getClass(self), sel, (IMP)classOther_c, "i20@0:8i16");
//types 可以只写类型不写内存大小
class_addMethod(object_getClass(self), sel, (IMP)classOther_c, "i@:i");
return NO;
}
return [super resolveClassMethod:sel];
}
@end
结果不再打印,-(void)test;
和+(int)classTest:(int)a;
分别是一个对象方法和有一个类方法,都是指在Person.h
文件中声明了,但是并没有实现;通过重写+(BOOL)resolveInstanceMethod:(SEL)sel
和 + (BOOL)resolveClassMethod:(SEL)sel
动态的添加了对象方法和类方法的实现,具体实现既可以是oc的方法也可以是c语言的函数。
(id)forwardingTargetForSelector:(SEL)aSelector
方法(目的是将该方法转发给对应targget,就是调用对应targget对应的同名方法),这时候如果返回的是一个类或者实例对象,就会调用该类或者实例对象同名的方法(直接用该对象或类调用其对应的方法);forwardingTargetForSelector
方法,就会调用(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
方法签名的方法,返回方法EncodeType(目的是指定方法的返回值和参数类型);(void)forwardInvocation:(NSInvocation *)anInvocation
,NSInvocation封装了一个方法调用,包括方法调用者,方法名,方法返回值及参数,指定方法调用者,指定方法,然后调用; 这样就可以让某个类或对象调用自己的方法来达到消息转发的目的。如果返回值为nil或者不匹配就会调用doesNotRecognizeSelector:
报方法找不到是错误。注意:
上面三个方法重写时,如果是转发类方法都是+
开头,如果是转发对象方法都是-
开头。示例:
//Person类声明
#import
@interface Person : NSObject
-(void)test;
+(void)classTest;
@end
//Person类实现
#import "Person.h"
#import "Dog.h"
#import
@implementation Person
//消息转发
//对象方法
-(id)forwardingTargetForSelector:(SEL)aSelector{
if (aSelector == @selector(test)) {
// return [[Dog alloc]init];//前提是方法名相同
return nil;
}
return [super forwardingTargetForSelector:aSelector];
}
//当forwardingTargetForSelector为nil的时候就会调用(方法签名的方法),返回值类型,参数类型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
if (aSelector == @selector(test)) {
// return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
//如果返回的是一个合理的方法签名(有值,并且返回值和参数类型一致),就调用forwardInvocation
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
//转发方法调用,NSInvocation封装了一个方法调用,包括方法调用者,方法名,方法返回值及参数
- (void)forwardInvocation:(NSInvocation *)anInvocation{
// anInvocation.target = [[Dog alloc]init];//指定对象目标
// [anInvocation invoke];//调用
if (anInvocation.selector == @selector(test)) {
anInvocation.selector = @selector(test1);//指定方法,如果不指定,默认是同名的方法(即不写就是调用Dog的test方法);
}
[anInvocation invokeWithTarget:[[Dog alloc]init]];
}
//类方法
+(id)forwardingTargetForSelector:(SEL)aSelector{
if (aSelector == @selector(classTest)) {
// return [Dog class];
// return object_getClass([[Dog alloc]init]);
return nil;
}
return [super forwardingTargetForSelector:aSelector];
}
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
if (aSelector == @selector(classTest)){
//return [NSMethodSignature signatureWithObjCTypes:"v@:"];
return [[Dog class] methodSignatureForSelector:@selector(classTest1)];
}
return [super methodSignatureForSelector:aSelector];
}
+ (void)forwardInvocation:(NSInvocation *)anInvocation{
if (anInvocation.selector == @selector(classTest)){
anInvocation.selector = @selector(classTest1);
}
[anInvocation invokeWithTarget:[Dog class]];
}
@end
//Dog类实现部分
#import "Dog.h"
@implementation Dog
- (void)test{
NSLog(@"%s",__func__);
}
+(void)classTest{
NSLog(@"%s",__func__);
}
- (void)test1{
NSLog(@"%s",__func__);
}
+(void)classTest1{
NSLog(@"%s",__func__);
}
@end
示例中并没有实现Person类的- (void)test
和+(void)classTest
,当调用这两个方法是通过消息转发调用Dog的- (void)test1
和+(void)classTest1
实现的。
(void)forwardInvocation:(NSInvocation *)anInvocation
的其他用法;forwardInvocation:
方法中自定义任何逻辑),也就是这时候就算没有实现对应的方法,也不会报方法找不到的错误。+ (void)forwardInvocation:(NSInvocation *)anInvocation{
NSLog(@"%s",__func__);
}
super
调用,底层会转换为objc_msgSendSuper
函数的调用,接收2个参数struct objc_super2
和*SEL
;objc_super2
的结构如下:receiver
是消息接收者,current_class
是receiver
的Class
对象。//super底层结构
struct objc_super {
__unsafe_unretained _Nonnull id receiver; // 消息接收者
__unsafe_unretained _Nonnull Class super_class; // 消息接收者的父类
};
[super message]
的底层实现 ,1.消息接收者仍然是当前类对象,2.从父类开始查找方法的实现。
//Man类是继承Person类
#import "Man.h"
@implementation Man
-(instancetype)init{
if (self = [super init]) {
NSLog(@"[self class] = %@", [self class]); // Man
NSLog(@"[self superclass] = %@", [self superclass]); // Person
NSLog(@"--------------------------------");
// objc_msgSendSuper({self, [MJPerson class]}, @selector(class));
NSLog(@"[super class] = %@", [super class]); // Man
NSLog(@"[super superclass] = %@", [super superclass]); // Person
}
return self;
}
@end
打印结果:
结果分析:class和superclass都是基类NSObject的方法(其中superclass是只读属性),在self调用这两个方法时,消息接收者的Man实例对象,所以返回的分别是Man和person;当super调用者两个方法时,也是给消息接收者发消息(objc_msgSendSuper({self, [MJPerson class]}, @selector(class));
),而此时消息接收者还是Man实例对象,所以结果和self调用一样;其中class
和superclass
的结构如下(简化后的):
//Class结构
- (Class)class
{
return object_getClass(self);
}
//superclass结构
- (Class)superclass
{
return class_getSuperclass(object_getClass(self));
}
动态创建一个类(参数:父类,类名,额外的内存空间)
Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)
注册一个类(要在类注册之前添加成员变量)
void objc_registerClassPair(Class cls)
销毁一个类
void objc_disposeClassPair(Class cls)
获取isa指向的Class
Class object_getClass(id obj)
设置isa指向的Class
Class object_setClass(id obj, Class cls)
判断一个OC对象是否为Class
BOOL object_isClass(id obj)
判断一个Class是否为元类
BOOL class_isMetaClass(Class cls)
获取父类
Class class_getSuperclass(Class cls)
获取一个实例变量信息
Ivar class_getInstanceVariable(Class cls, const char *name)
拷贝实例变量列表(最后需要调用free释放)
Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
设置和获取成员变量的值
void object_setIvar(id obj, Ivar ivar, id value)
id object_getIvar(id obj, Ivar ivar)
动态添加成员变量(已经注册的类是不能动态添加成员变量的)
BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types)
获取成员变量的相关信息
const char *ivar_getName(Ivar v)
const char *ivar_getTypeEncoding(Ivar v)
获取一个属性
objc_property_t class_getProperty(Class cls, const char *name)
拷贝属性列表(最后需要调用free释放)
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
动态添加属性
BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
unsigned int attributeCount)
动态替换属性
void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
unsigned int attributeCount)
获取属性的一些信息
const char *property_getName(objc_property_t property)
const char *property_getAttributes(objc_property_t property)
获得一个实例方法、类方法
Method class_getInstanceMethod(Class cls, SEL name)
Method class_getClassMethod(Class cls, SEL name)
方法实现相关操作
IMP class_getMethodImplementation(Class cls, SEL name)
IMP method_setImplementation(Method m, IMP imp)
void method_exchangeImplementations(Method m1, Method m2)
拷贝方法列表(最后需要调用free释放)
Method *class_copyMethodList(Class cls, unsigned int *outCount)
动态添加方法
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
动态替换方法
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
获取方法的相关信息(带有copy的需要调用free去释放)
SEL method_getName(Method m)
IMP method_getImplementation(Method m)
const char *method_getTypeEncoding(Method m)
unsigned int method_getNumberOfArguments(Method m)
char *method_copyReturnType(Method m)
char *method_copyArgumentType(Method m, unsigned int index)
选择器相关
const char *sel_getName(SEL sel)
SEL sel_registerName(const char *str)
用block作为方法实现
IMP imp_implementationWithBlock(id block)
id imp_getBlock(IMP anImp)
BOOL imp_removeBlock(IMP anImp)
unsigned int count;
Ivar *ivars = class_copyIvarList([UITextField class], &count);
for (int i = 0; i < count; i++) {
// 取出i位置的成员变量
Ivar ivar = ivars[i];
NSLog(@"%s %s", ivar_getName(ivar), ivar_getTypeEncoding(ivar));
}
free(ivars);
self.textField.placeholder = @"请输入用户名";
[self.textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
@implementation NSObject (Json)
+ (instancetype)mj_objectWithJson:(NSDictionary *)json
{
id obj = [[self alloc] init];
unsigned int count;
Ivar *ivars = class_copyIvarList(self, &count);
for (int i = 0; i < count; i++) {
// 取出i位置的成员变量
Ivar ivar = ivars[i];
NSMutableString *name = [NSMutableString stringWithUTF8String:ivar_getName(ivar)];
[name deleteCharactersInRange:NSMakeRange(0, 1)];
// 设值
id value = json[name];
if ([name isEqualToString:@"ID"]) {
value = json[@"id"];
}
[obj setValue:value forKey:name];
}
free(ivars);
return obj;
}
class_replaceMethod
,method_exchangeImplementations
两个函数都可以替换方法的实现。void test(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [[MJPerson alloc] init];
Method runMethod = class_getInstanceMethod([MJPerson class], @selector(run));
Method testMethod = class_getInstanceMethod([MJPerson class], @selector(test));
method_exchangeImplementations(runMethod, testMethod);
[person run];
test();
}
return 0;
}
void myrun()
{
NSLog(@"---myrun");
}
void test(void)
{
MJPerson *person = [[MJPerson alloc] init];
//用myrun来替换run方法的实现
// class_replaceMethod([MJPerson class], @selector(run), (IMP)myrun, "v");
//用block里面的代码替换run方法的实现
class_replaceMethod([MJPerson class], @selector(run), imp_implementationWithBlock(^{
NSLog(@"123123");
}), "v");
[person run];
}
一般是hook系统的方法实现,要实现系统的方法同时添加进去自己的操作。
@implementation UIControl (Extension)
+ (void)load
{
// hook:钩子函数
Method method1 = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
Method method2 = class_getInstanceMethod(self, @selector(mj_sendAction:to:forEvent:));
method_exchangeImplementations(method1, method2);
}
- (void)mj_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
NSLog(@"%@-%@-%@", self, target, NSStringFromSelector(action));
// 调用系统原来的实现
[self mj_sendAction:action to:target forEvent:event];
// [target performSelector:action];
// if ([self isKindOfClass:[UIButton class]]) {
// // 拦截了所有按钮的事件
//
// }
}
@end
isMemberOfClass
和isKindOfClass
区别,以及使用注意点。先来看一下他们的底层实现:
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
+ (BOOL)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls;
}
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
可以看出isMemberOfClass
就是比较是不是当前类对象(-开头的)或者原类对象(+开头的);isKindOfClass
比较是不是当前(原)类对象及父类的(原)类对象。
NSLog(@"%d", [[NSObject class] isKindOfClass:[NSObject class]]);
NSLog(@"%d", [[NSObject class] isMemberOfClass:[NSObject class]]);
NSLog(@"%d", [[Person class] isKindOfClass:[Person class]]);
NSLog(@"%d", [[Person class] isMemberOfClass:[Person class]]);
可以打印一下,输出的都是1,0,0,0;上面调用的都是+好开头的类方法;第一个为YES
,因为NSObject原类
的superclass
指针指向的NSObject类
(这是一个特例);第二个为NO,因为类调用+isMemberOfClass
,是比较原类的,二后面传进来的参数是[NSObject class]
是类(二应该是通原类的class方法比较),所以为NO;第三个和第四个都为NO原因同第二个。
-号
开头的,也就是对象调用上面两个方法,后面传入的参数应该为类对象(比较实例对象指向的class(类)与后面传进来的class);+号
开头的,也就是类调用上面两个方法,后面传入的应该为原类对象(比较类对象指向的class(原类)与后面传进来的原类class);object_getClass(id _Nullable obj)
如果传进去的是实例对象则获得的是类对象,传进去的是类对象的获得的是原类对象。 NSLog(@"%d", [NSObject isMemberOfClass:object_getClass([NSObject class])]);
NSLog(@"%d", [Person isKindOfClass:object_getClass([Person class])]);
NSLog(@"%d", [Person isMemberOfClass:object_getClass([Person class])]);
OC中的方法调用其实都是转成了objc_msgSend函数的调用,给receiver(方法调用者)发送了一条消息(selector方法名);objc_msgSend底层有3大阶段,消息发送(当前类、父类中查找)、动态方法解析、消息转发。
OC是一门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行
OC的动态性就是由Runtime来支撑和实现的,Runtime是一套C语言的API,封装了很多动态性相关的函数,平时编写的OC代码,底层都是转换成了Runtime API进行调用;
具体应用:
利用关联对象(AssociatedObject)给分类添加属性,
遍历类的所有成员变量(修改textfield的占位文字颜色、字典转模型、自动归档解档),
交换方法实现(交换系统的方法),
利用消息转发机制解决方法找不到的异常问题。