请稍侯

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_classobjc_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中只有nameattributes两个成员属性分别是名字和类型,property_t可以帮助我们动态的获取和修改类的变量。

objc.h里,可以看到Classobjc_objectid的定义:

  • 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专门用来存储小的对象,例如NSNumberNSDate以及长度小于7字符的NString
  • Tagged Pointer指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象了,它只是一个披着对象皮的普通变量而已。所以,它的内存并不存储在堆中,也不需要malloc和free;

NSNumber、NSDate、NSString 自动转伪指针的注册流程:

  1. 判断objc_debug_taggedpointer_mask是否为0,判断是否开启Tagger Pointer,如果是禁用,直接回终止启动;
  2. 根据tag去取出类指针,若传递的是一个无效的tag,没有取到正确的类指针,还是终止启动;
  3. 尝试通过Class oldCls = *slot去获取该指针指向的类,若要注册的类与该处获取的类不是同一个,那么也是终止启动;
  4. 通过*slot = cls将入参cls类赋值给指针slot指向的位置;
  5. 若注册的不是基础类型,则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