Objective-C中Class、NSObject、id的本质
06 December 2020
Objective-C中Class、NSObject、id的本质
Class 与 objc_class
objc 中的结构体

objc_ivar有四个成员属性,其中ivar_name,ivar_type,ivar_offset分别代表了属性的名称,类型,内存偏移量;objc_class的定义是以结构的形式实现,包含有类本身及父类的信息,属性列表、方法列表、缓存和协议列表;objc_class中objc_ivar_list与其中的objc_ivar用于实现类的属性列表;objc_method_list与其中的objc_method用于实现类的方法列表;objc_protocol_list以及Protocol的数组去实现协议列表,不同的是Protocol的实现不再是一个结构,而是一个继承自NSObject的类;objc_category定义了Category的数据结构,通过category_name区分不同的category,通过class_name关联相关的类;property_t中只有name和attributes两个成员属性分别是名字和类型,property_t可以帮助我们动态的获取和修改类的变量。
在objc.h里,可以看到Class、objc_object、id的定义: 
Class实际上是 结构体objc_class的指针类型;objc_object里有个Class类型的isa,我们知道isa是指向objc_class结构体的指针,那这个isa的作用其实就是标记这个objc_object对象自己属于某种类型,所以当我们要去判断一个objc_object对象是哪种类型时,只要看它的isa指向谁就可以了;id实际上是 结构体objc_object的指针类型;
在runtime.h里,可以看到objc_class的定义: 
NSObject定义 
SEL 与 IMP 的本质
SEL 定义为结构体objc_selector 的指针,但其本质上是映射为C字符串,因为使用printf("%s",@selector(abcd));会输出abcd,所以SEL就是方法名。
- 在iOS 和MAC 中没有找到
objc_selector的定义,但其SEL就只是一个char *类型的字符串,SEL是映射为C字符串; - 但在GNU OC中的定义
objc_selector有两个成员,一个是sel_id,一个是sel_types的C字符串;

IMP 实际上是一个函数指针。
BOOL本质上是bool 或 signed char

Method 的本质
Method本质上是objc_method的指针,objc_method_list是个单链表结构; 
使用 method_invoke 与 objc_msgSend 两种方式调用函数
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <objc/message.h>
@interface HelloTest : NSObject
- (int)handleName:(NSString *)name andValue:(NSNumber *)value;
@end
@implementation HelloTest
- (int)handleName:(NSString *)name andValue:(NSNumber *)value {
NSLog(@"handleName:%@", name);
return value.intValue;
}
@end
int main(int argc, const char * argv[]) {
HelloTest *m = [HelloTest new];
// 获取方法
Method method = class_getInstanceMethod([m class], @selector(handleName:andValue:));
// 调用函数:method_invoke 方式
int returnValue1 = ((int (*)(id, Method, NSString *, NSNumber *))method_invoke) ((id)m, method, @"测试 method_invoke", @10);
NSLog(@"method_invoke返回值: %d", returnValue1);
// 调用函数:objc_msgSend 方式
int returnValue2 = ((int (*)(id, SEL, NSString *, NSNumber *))objc_msgSend) ((id)m,@selector(handleName:andValue:),@"测试 objc_msgSend",@100);
NSLog(@"objc_msgSend返回值: %d", returnValue2);
return 0;
}
标记指针
- 标记指针(Tagged Pointer) 是一个伪对象,所以内部并没有
isa指针。Tagged Pointer 为我们带来了 3 倍存储空间的节省,以及 106 倍的访问速度; - 标记指针是把对象的指针拆成两部分,一部分直接保存对象的数据,另一部分作为特殊标记,表示这是一个特别的指针,不指向任何一个地址,所以也叫伪指针;
- Tagged Pointer专门用来存储小的对象,例如
NSNumber、NSDate以及长度小于7字符的NString; - Tagged Pointer指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象了,它只是一个披着对象皮的普通变量而已。所以,它的内存并不存储在堆中,也不需要malloc和free;
NSNumber、NSDate、NSString 自动转伪指针的注册流程:
- 判断
objc_debug_taggedpointer_mask是否为0,判断是否开启Tagger Pointer,如果是禁用,直接回终止启动; - 根据tag去取出类指针,若传递的是一个无效的tag,没有取到正确的类指针,还是终止启动;
- 尝试通过
Class oldCls = *slot去获取该指针指向的类,若要注册的类与该处获取的类不是同一个,那么也是终止启动; - 通过
*slot = cls将入参cls类赋值给指针slot指向的位置; - 若注册的不是基础类型,则
OBJC_TAG_RESERVED_7存储占位类OBJC_CLASS_$___NSUnrecognizedTaggedPointer;
参考:Objective-C中伪指针Tagged PointerTagged Pointer
NSObject实现源代码
@implementation NSObject
+ (void)initialize {
}
+ (id)self {
return (id)self;
}
- (id)self {
return self;
}
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
+ (Class)superclass {
return self->superclass;
}
- (Class)superclass {
return [self class]->superclass;
}
+ (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;
}
+ (BOOL)isSubclassOfClass:(Class)cls {
for (Class tcls = self; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
+ (BOOL)isAncestorOfObject:(NSObject *)obj {
for (Class tcls = [obj class]; tcls; tcls = tcls->superclass) {
if (tcls == self) return YES;
}
return NO;
}
+ (BOOL)instancesRespondToSelector:(SEL)sel {
return class_respondsToSelector_inst(nil, sel, self);
}
+ (BOOL)respondsToSelector:(SEL)sel {
return class_respondsToSelector_inst(self, sel, self->ISA());
}
- (BOOL)respondsToSelector:(SEL)sel {
return class_respondsToSelector_inst(self, sel, [self class]);
}
+ (BOOL)conformsToProtocol:(Protocol *)protocol {
if (!protocol) return NO;
for (Class tcls = self; tcls; tcls = tcls->superclass) {
if (class_conformsToProtocol(tcls, protocol)) return YES;
}
return NO;
}
- (BOOL)conformsToProtocol:(Protocol *)protocol {
if (!protocol) return NO;
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (class_conformsToProtocol(tcls, protocol)) return YES;
}
return NO;
}
+ (NSUInteger)hash {
return _objc_rootHash(self);
}
- (NSUInteger)hash {
return _objc_rootHash(self);
}
+ (BOOL)isEqual:(id)obj {
return obj == (id)self;
}
- (BOOL)isEqual:(id)obj {
return obj == self;
}
+ (BOOL)isFault {
return NO;
}
- (BOOL)isFault {
return NO;
}
+ (BOOL)isProxy {
return NO;
}
- (BOOL)isProxy {
return NO;
}
+ (IMP)instanceMethodForSelector:(SEL)sel {
if (!sel) [self doesNotRecognizeSelector:sel];
return class_getMethodImplementation(self, sel);
}
+ (IMP)methodForSelector:(SEL)sel {
if (!sel) [self doesNotRecognizeSelector:sel];
return object_getMethodImplementation((id)self, sel);
}
- (IMP)methodForSelector:(SEL)sel {
if (!sel) [self doesNotRecognizeSelector:sel];
return object_getMethodImplementation(self, sel);
}
+ (BOOL)resolveClassMethod:(SEL)sel {
return NO;
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
return NO;
}
// Replaced by CF (throws an NSException)
+ (void)doesNotRecognizeSelector:(SEL)sel {
_objc_fatal("+[%s %s]: unrecognized selector sent to instance %p",
class_getName(self), sel_getName(sel), self);
}
// Replaced by CF (throws an NSException)
- (void)doesNotRecognizeSelector:(SEL)sel {
_objc_fatal("-[%s %s]: unrecognized selector sent to instance %p",
object_getClassName(self), sel_getName(sel), self);
}
+ (id)performSelector:(SEL)sel {
if (!sel) [self doesNotRecognizeSelector:sel];
return ((id(*)(id, SEL))objc_msgSend)((id)self, sel);
}
+ (id)performSelector:(SEL)sel withObject:(id)obj {
if (!sel) [self doesNotRecognizeSelector:sel];
return ((id(*)(id, SEL, id))objc_msgSend)((id)self, sel, obj);
}
+ (id)performSelector:(SEL)sel withObject:(id)obj1 withObject:(id)obj2 {
if (!sel) [self doesNotRecognizeSelector:sel];
return ((id(*)(id, SEL, id, id))objc_msgSend)((id)self, sel, obj1, obj2);
}
- (id)performSelector:(SEL)sel {
if (!sel) [self doesNotRecognizeSelector:sel];
return ((id(*)(id, SEL))objc_msgSend)(self, sel);
}
- (id)performSelector:(SEL)sel withObject:(id)obj {
if (!sel) [self doesNotRecognizeSelector:sel];
return ((id(*)(id, SEL, id))objc_msgSend)(self, sel, obj);
}
- (id)performSelector:(SEL)sel withObject:(id)obj1 withObject:(id)obj2 {
if (!sel) [self doesNotRecognizeSelector:sel];
return ((id(*)(id, SEL, id, id))objc_msgSend)(self, sel, obj1, obj2);
}
// Replaced by CF (returns an NSMethodSignature)
+ (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)sel {
_objc_fatal("+[NSObject instanceMethodSignatureForSelector:] "
"not available without CoreFoundation");
}
// Replaced by CF (returns an NSMethodSignature)
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
_objc_fatal("+[NSObject methodSignatureForSelector:] "
"not available without CoreFoundation");
}
// Replaced by CF (returns an NSMethodSignature)
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
_objc_fatal("-[NSObject methodSignatureForSelector:] "
"not available without CoreFoundation");
}
+ (void)forwardInvocation:(NSInvocation *)invocation {
[self doesNotRecognizeSelector:(invocation ? [invocation selector] : 0)];
}
- (void)forwardInvocation:(NSInvocation *)invocation {
[self doesNotRecognizeSelector:(invocation ? [invocation selector] : 0)];
}
+ (id)forwardingTargetForSelector:(SEL)sel {
return nil;
}
- (id)forwardingTargetForSelector:(SEL)sel {
return nil;
}
// Replaced by CF (returns an NSString)
+ (NSString *)description {
return nil;
}
// Replaced by CF (returns an NSString)
- (NSString *)description {
return nil;
}
+ (NSString *)debugDescription {
return [self description];
}
- (NSString *)debugDescription {
return [self description];
}
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}
+ (id)retain {
return (id)self;
}
// Replaced by ObjectAlloc
- (id)retain {
return _objc_rootRetain(self);
}
+ (BOOL)_tryRetain {
return YES;
}
// Replaced by ObjectAlloc
- (BOOL)_tryRetain {
return _objc_rootTryRetain(self);
}
+ (BOOL)_isDeallocating {
return NO;
}
- (BOOL)_isDeallocating {
return _objc_rootIsDeallocating(self);
}
+ (BOOL)allowsWeakReference {
return YES;
}
+ (BOOL)retainWeakReference {
return YES;
}
- (BOOL)allowsWeakReference {
return ! [self _isDeallocating];
}
- (BOOL)retainWeakReference {
return [self _tryRetain];
}
+ (oneway void)release {
}
// Replaced by ObjectAlloc
- (oneway void)release {
_objc_rootRelease(self);
}
+ (id)autorelease {
return (id)self;
}
// Replaced by ObjectAlloc
- (id)autorelease {
return _objc_rootAutorelease(self);
}
+ (NSUInteger)retainCount {
return ULONG_MAX;
}
- (NSUInteger)retainCount {
return _objc_rootRetainCount(self);
}
+ (id)alloc {
return _objc_rootAlloc(self);
}
// Replaced by ObjectAlloc
+ (id)allocWithZone:(struct _NSZone *)zone {
return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
}
// Replaced by CF (throws an NSException)
+ (id)init {
return (id)self;
}
- (id)init {
return _objc_rootInit(self);
}
// Replaced by CF (throws an NSException)
+ (void)dealloc {
}
// Replaced by NSZombies
- (void)dealloc {
_objc_rootDealloc(self);
}
// Previously used by GC. Now a placeholder for binary compatibility.
- (void) finalize {
}
+ (struct _NSZone *)zone {
return (struct _NSZone *)_objc_rootZone(self);
}
- (struct _NSZone *)zone {
return (struct _NSZone *)_objc_rootZone(self);
}
+ (id)copy {
return (id)self;
}
+ (id)copyWithZone:(struct _NSZone *)zone {
return (id)self;
}
- (id)copy {
return [(id)self copyWithZone:nil];
}
+ (id)mutableCopy {
return (id)self;
}
+ (id)mutableCopyWithZone:(struct _NSZone *)zone {
return (id)self;
}
- (id)mutableCopy {
return [(id)self mutableCopyWithZone:nil];
}
@end
