Objective-C中的对象,简称OC对象,主要可以分为3种
1. instance对象(实例对象)
-
instance
对象就是通过类alloc出来的对象,每次调用alloc都会产生新的instance对象 - 代码表现
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
- object1, object2都是NSObject的
instance
对象(实例对象) - 它们是不同的两个对象,分别占据着两块不同的内存
-
instace
对象在内存中存储的信息包括-
isa
指针 - 其它
成员变量
-
2. class对象(类对象)
- 代码表现
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
Class objectClass1 = [object1 class];
Class objectClass2 = [object2 class];
Class objectClass3 = [NSObject class];
Class objectClass4 = object_getClass(object1); // Runtime
Class objectClass5 = object_getClass(object2); // Runtime
- objectClass1 ~ objectClass5都是NSObject的
class
对象(类对象),class方法返回的一直是class
对象 - 它们都是同一个对象.每个类在内存中有且只有一个
class
对象 -
class
对象在内存中存储的信息主要包括-
isa
指针 -
superclass
指针 - 类的属性信息(
@property
),类的对象方法信息(instance method
) - 类的协议信息(
protocol
),类的成员变量信息(ivar
)
-
3. meta-class对象(元类对象)
- 代码表现
Class objectMetaClass = object_getClass([NSObject class]); // Runtime
- objectMetaClass是
NSObject
的meta-class
对象 - 每个类在内存中有且只有一个
meta-class
对象 -
meta-class
对象和class
对象的内存结构是一样的,但是用途不一样,在内存中存储的信息主要包括-
isa
指针 -
superclass
指针 - 类的类方法信息(
class method
)
-
- 查看
Class
是否为meta-class
BOOL result = class_isMetaClass([NSObject class]); // Runtime
object_getClass的内部实现
image.png- 因为Objective-C和swift的混编问题,一些底层实现有所变化
Class objc_getClass(const char *aClassName)
{
if (!aClassName) return Nil;
// NO unconnected, YES class handler
return look_up_class(aClassName, NO, YES);
}
Class look_up_class(const char *name,
bool includeUnconnected __attribute__((unused)),
bool includeClassHandler __attribute__((unused)))
{
// 传过来的类名为空的话,直接返回nil
if (!name) return nil;
// 定义Class结果result
Class result;
// 定义未实现过变量unrealized
bool unrealized;
{
// 加锁
runtimeLock.lock();
// 获取非swift的Class
result = getClassExceptSomeSwift(name);
// 如果result有值,且result类的方法实现过了
unrealized = result && !result->isRealized();
if (unrealized) {
// 未实现的话,调用realizeClassMaybeSwiftAndUnlock实现一下,同时解锁
result = realizeClassMaybeSwiftAndUnlock(result, runtimeLock);
// runtimeLock is now unlocked
} else {
// 解锁
runtimeLock.unlock();
}
}
if (!result) {
// Ask Swift about its un-instantiated classes.
// We use thread-local storage to prevent infinite recursion
// if the hook function provokes another lookup of the same name
// (for example, if the hook calls objc_allocateClassPair)
auto *tls = _objc_fetch_pthread_data(true);
// Stop if this thread is already looking up this name.
for (unsigned i = 0; i < tls->classNameLookupsUsed; i++) {
if (0 == strcmp(name, tls->classNameLookups[i])) {
return nil;
}
}
// Save this lookup in tls.
if (tls->classNameLookupsUsed == tls->classNameLookupsAllocated) {
tls->classNameLookupsAllocated =
(tls->classNameLookupsAllocated * 2 ?: 1);
size_t size = tls->classNameLookupsAllocated *
sizeof(tls->classNameLookups[0]);
tls->classNameLookups = (const char **)
realloc(tls->classNameLookups, size);
}
tls->classNameLookups[tls->classNameLookupsUsed++] = name;
// Call the hook.
Class swiftcls = nil;
if (GetClassHook.get()(name, &swiftcls)) {
ASSERT(swiftcls->isRealized());
result = swiftcls;
}
// Erase the name from tls.
unsigned slot = --tls->classNameLookupsUsed;
ASSERT(slot >= 0 && slot < tls->classNameLookupsAllocated);
ASSERT(name == tls->classNameLookups[slot]);
tls->classNameLookups[slot] = nil;
}
return result;
}
说说isa
-
instance
对象的isa
指向class
- 当调用
对象方法
时,通过instance
的isa
找到class
,最后找到对象方法
的实现进行调用
- 当调用
-
class
的isa
指向meta-class
- 当调用
类方法
时,通过class
的isa
找到meta-class
,最后找到类方法
的实现进行调用
- 当调用
-
meta-class
的isa
指向基类的meta-class
说说superclass
-
class
的superclass
指向meta-class
- 如果没有父类,
superclass
指针为nil
- 如果没有父类,
-
meta-class
的superclass
指向基类的meta-class
- 基类的
meta-class
的superclass
指向基类的class
- 基类的
-
例子1(实例对象调用父类的对象方法),Student的instance实例调用Person的对象方法流程
image.png -
例子2(类对象调用父类的类方法),Student的class对象调用Person类的类方法流程
image.png
经典的isa和superclass图解
image.png-
intance调用对象方法的轨迹
- isa找到class,方法不存在,就通过superclass找父类,一直找,直到所有的父类找完都没有这个对象方法的实现时,再经过runtime的动态方法解析和消息转发,如果都没有,就会报
unrecognized selector sent to instance 0xxxxxxxxx
- isa找到class,方法不存在,就通过superclass找父类,一直找,直到所有的父类找完都没有这个对象方法的实现时,再经过runtime的动态方法解析和消息转发,如果都没有,就会报
-
class调用类方法的轨迹
- isa找meta-class,方法不存在,就通过superclass找父类
- 这种有一种特殊情况🙊,找到meta-class的都没有找到时,因为
meta-class
的superclass
指向基类的meta-class
的,所以会调用基类的meta-class
的类方法.这里不用奇怪明明调用的是类方法,最后却调用了基类的对象方法.- 示例代码如下
#import <Foundation/Foundation.h> #import <objc/objc.h> @interface XYPerson : NSObject + (void)test; @end @implementation XYPerson @end @interface NSObject (Test) + (void)test; @end @implementation NSObject (Test) - (void)test { NSLog(@"-[NSObject test] - %p", self); } @end int main(int argc, const char * argv[]) { @autoreleasepool { // [XYPerson class] - 0x1000041e0 NSLog(@"[XYPerson class] - %p", [XYPerson class]); // [NSObject class] - 0x7fff90dd4118 NSLog(@"[NSObject class] - %p", [NSObject class]); // -[NSObject test] - 0x1000041e0 [XYPerson test]; // objc_msgSend([XYPerson class], @selector(test)); // -[NSObject test] - 0x7fff90dd4118 [NSObject test]; // objc_msgSend([NSObject class], @selector(test)); } return 0; }
说说isa和superclass细节
- 代码示意
// MJPerson类对象的地址:0x00000001000014c8
// MJPerson实例对象的isa:0x001d8001000014c9(为什么地址有差别,需要要与上一个 ISA_MASK 才是真实的值)
// isa & ISA_MASK:0x00000001000014c8
MJPerson *person = [[MJPerson alloc] init];
Class personClass = [MJPerson class];
Class personMetaClass = object_getClass(personClass);
-
从64bit开始,isa需要进行一次位运算,才能计算出真实的isa地址值
- isa & ISA_MASK
-
ISA_MASK的值
image.png
-
superclass不需要做此操作,直接就是对应的地址值
isa和superclass-class和meta-class的结构
- 代码结构
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
struct objc_class : objc_object {
//...
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
class_rw_t *data() const {
return bits.data();
}
...
}
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint16_t witness;
#if SUPPORT_INDEXED_ISA
uint16_t index;
#endif
explicit_atomic<uintptr_t> ro_or_rw_ext;
Class firstSubclass;
Class nextSiblingClass;
};
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
union {
const uint8_t * ivarLayout;
Class nonMetaclass;
};
explicit_atomic<const char *> name;
// With ptrauth, this is signed if it points to a small list, but
// may be unsigned if it points to a big list.
void *baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
};
-
图片结构
image.png
模拟class内部的结构
#import <Foundation/Foundation.h>
#ifndef MJClassInfo_h
#define MJClassInfo_h
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# endif
#if __LP64__
typedef uint32_t mask_t;
#else
typedef uint16_t mask_t;
#endif
typedef uintptr_t cache_key_t;
struct bucket_t {
cache_key_t _key;
IMP _imp;
};
struct cache_t {
bucket_t *_buckets;
mask_t _mask;
mask_t _occupied;
};
struct entsize_list_tt {
uint32_t entsizeAndFlags;
uint32_t count;
};
struct method_t {
SEL name;
const char *types;
IMP imp;
};
struct method_list_t : entsize_list_tt {
method_t first;
};
struct ivar_t {
int32_t *offset;
const char *name;
const char *type;
uint32_t alignment_raw;
uint32_t size;
};
struct ivar_list_t : entsize_list_tt {
ivar_t first;
};
struct property_t {
const char *name;
const char *attributes;
};
struct property_list_t : entsize_list_tt {
property_t first;
};
struct chained_property_list {
chained_property_list *next;
uint32_t count;
property_t list[0];
};
typedef uintptr_t protocol_ref_t;
struct protocol_list_t {
uintptr_t count;
protocol_ref_t list[0];
};
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize; // instance对象占用的内存空间
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name; // 类名
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars; // 成员变量列表
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
};
struct class_rw_t {
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_list_t * methods; // 方法列表
property_list_t *properties; // 属性列表
const protocol_list_t * protocols; // 协议列表
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
};
#define FAST_DATA_MASK 0x00007ffffffffff8UL
struct class_data_bits_t {
uintptr_t bits;
public:
class_rw_t* data() {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
};
/* OC对象 */
struct mj_objc_object {
void *isa;
};
/* 类对象 */
struct mj_objc_class : mj_objc_object {
Class superclass;
cache_t cache;
class_data_bits_t bits;
public:
class_rw_t* data() {
return bits.data();
}
mj_objc_class* metaClass() {
return (mj_objc_class *)((long long)isa & ISA_MASK);
}
};
#endif /* MJClassInfo_h */
// objective-c++
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "MJClassInfo.h"
// MJPerson
@interface MJPerson : NSObject <NSCopying>
{
@public
int _age;
}
@property (nonatomic, assign) int no;
- (void)personInstanceMethod;
+ (void)personClassMethod;
@end
@implementation MJPerson
- (void)test
{
}
- (void)personInstanceMethod
{
}
+ (void)personClassMethod
{
}
- (id)copyWithZone:(NSZone *)zone
{
return nil;
}
@end
// MJStudent
@interface MJStudent : MJPerson <NSCoding>
{
@public
int _weight;
}
@property (nonatomic, assign) int height;
- (void)studentInstanceMethod;
+ (void)studentClassMethod;
@end
@implementation MJStudent
- (void)test
{
}
- (void)studentInstanceMethod
{
}
+ (void)studentClassMethod
{
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
return nil;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJStudent *stu = [[MJStudent alloc] init];
stu->_weight = 10;
mj_objc_class *studentClass = (__bridge mj_objc_class *)([MJStudent class]);
mj_objc_class *personClass = (__bridge mj_objc_class *)([MJPerson class]);
class_rw_t *studentClassData = studentClass->data();
class_rw_t *personClassData = personClass->data();
class_rw_t *studentMetaClassData = studentClass->metaClass()->data();
class_rw_t *personMetaClassData = personClass->metaClass()->data();
NSLog(@"1111");
}
return 0;
}
面试题
- 对象的isa指针指向哪里?
-
instance
对象的isa指向class
对象 -
class
对象的isa指向meta-class
对象 -
meta-class
对象的isa指向基类
的meta-class
对象
-
- OC的类信息存放在哪里?
-
对象方法
,属性,成员变量,协议信息,存放在class
对象中 -
类方法
,存放在meta-class
对象中 -
成员变量
的具体值,存放在instance
对象
-
网友评论