Objective-C中的對象,簡稱OC對象,主要可以分為3種
1. instance對象(實例對象)
-
instance
對象就是通過類alloc出來的對象,每次調(diào)用alloc都會產(chǎn)生新的instance對象 - 代碼表現(xiàn)
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
- object1, object2都是NSObject的
instance
對象(實例對象) - 它們是不同的兩個對象,分別占據(jù)著兩塊不同的內(nèi)存
-
instace
對象在內(nèi)存中存儲的信息包括-
isa
指針 - 其它
成員變量
-
2. class對象(類對象)
- 代碼表現(xiàn)
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
對象 - 它們都是同一個對象.每個類在內(nèi)存中有且只有一個
class
對象 -
class
對象在內(nèi)存中存儲的信息主要包括-
isa
指針 -
superclass
指針 - 類的屬性信息(
@property
),類的對象方法信息(instance method
) - 類的協(xié)議信息(
protocol
),類的成員變量信息(ivar
)
-
3. meta-class對象(元類對象)
- 代碼表現(xiàn)
Class objectMetaClass = object_getClass([NSObject class]); // Runtime
- objectMetaClass是
NSObject
的meta-class
對象 - 每個類在內(nèi)存中有且只有一個
meta-class
對象 -
meta-class
對象和class
對象的內(nèi)存結構是一樣的,但是用途不一樣,在內(nèi)存中存儲的信息主要包括-
isa
指針 -
superclass
指針 - 類的類方法信息(
class method
)
-
- 查看
Class
是否為meta-class
BOOL result = class_isMetaClass([NSObject class]); // Runtime
object_getClass的內(nèi)部實現(xiàn)
- 因為Objective-C和swift的混編問題,一些底層實現(xiàn)有所變化
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;
// 定義未實現(xiàn)過變量unrealized
bool unrealized;
{
// 加鎖
runtimeLock.lock();
// 獲取非swift的Class
result = getClassExceptSomeSwift(name);
// 如果result有值,且result類的方法實現(xiàn)過了
unrealized = result && !result->isRealized();
if (unrealized) {
// 未實現(xiàn)的話,調(diào)用realizeClassMaybeSwiftAndUnlock實現(xiàn)一下,同時解鎖
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
- 當調(diào)用
對象方法
時,通過instance
的isa
找到class
,最后找到對象方法
的實現(xiàn)進行調(diào)用
- 當調(diào)用
-
class
的isa
指向meta-class
- 當調(diào)用
類方法
時,通過class
的isa
找到meta-class
,最后找到類方法
的實現(xiàn)進行調(diào)用
- 當調(diào)用
-
meta-class
的isa
指向基類的meta-class
說說superclass
-
class
的superclass
指向meta-class
- 如果沒有父類,
superclass
指針為nil
- 如果沒有父類,
-
meta-class
的superclass
指向基類的meta-class
- 基類的
meta-class
的superclass
指向基類的class
- 基類的
-
例子1(實例對象調(diào)用父類的對象方法),Student的instance實例調(diào)用Person的對象方法流程
-
例子2(類對象調(diào)用父類的類方法),Student的class對象調(diào)用Person類的類方法流程
經(jīng)典的isa和superclass圖解
-
intance調(diào)用對象方法的軌跡
- isa找到class,方法不存在,就通過superclass找父類,一直找,直到所有的父類找完都沒有這個對象方法的實現(xiàn)時,再經(jīng)過runtime的動態(tài)方法解析和消息轉(zhuǎn)發(fā),如果都沒有,就會報
unrecognized selector sent to instance 0xxxxxxxxx
- isa找到class,方法不存在,就通過superclass找父類,一直找,直到所有的父類找完都沒有這個對象方法的實現(xiàn)時,再經(jīng)過runtime的動態(tài)方法解析和消息轉(zhuǎn)發(fā),如果都沒有,就會報
-
class調(diào)用類方法的軌跡
- isa找meta-class,方法不存在,就通過superclass找父類
- 這種有一種特殊情況??,找到meta-class的都沒有找到時,因為
meta-class
的superclass
指向基類的meta-class
的,所以會調(diào)用基類的meta-class
的類方法.這里不用奇怪明明調(diào)用的是類方法,最后卻調(diào)用了基類的對象方法.- 示例代碼如下
#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細節(jié)
- 代碼示意
// 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的值
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;
};
-
圖片結構
模擬class內(nèi)部的結構
#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對象占用的內(nèi)存空間
#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; // 協(xié)議列表
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的類信息存放在哪里?
-
對象方法
,屬性,成員變量,協(xié)議信息,存放在class
對象中 -
類方法
,存放在meta-class
對象中 -
成員變量
的具體值,存放在instance
對象
-