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