1.isa,superClass总结
isa
isa和superClass总结
2.实例
实例1
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>
// MJPerson
@interface MJPerson : NSObject
{
int _age;
}
@property (nonatomic, assign) int no;
- (void)personInstanceMethod;
+ (void)personClassMethod;
@end
@implementation MJPerson
- (void)personInstanceMethod
{
}
+ (void)personClassMethod
{
}
@end
// MJStudent
@interface MJStudent : MJPerson
{
int _weight;
}
@property (nonatomic, assign) int height;
- (void)studentInstanceMethod;
+ (void)studentClassMethod;
@end
@implementation MJStudent
- (void)studentInstanceMethod
{
}
+ (void)studentClassMethod
{
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJStudent *stu =[[MJStudent alloc]init];
[stu studentInstanceMethod];
//stu对象不保存对象方法,通过isa指针找到class对象,找到对象方法。
//这个方法底层实现:objc_msgSend(stu, @selector(studentInstanceMethod));
//C/C++源码: objc_msgSend(stu,Sel_registerName("studentInstanceMethod"));
//为啥不直接[MJStudent studentInstanceMethod]; ? 语法设计问题,这样不能体现面对对象思想
[MJStudent studentClassMethod];
//MJStudent的class对象没有类方法,通过isa指针找到元类的类方法
//底层实现类似:objc_msgSend([MJStudent class], @selector(studentClassMethod));
[MJStudent personClassMethod];
//底层实现类似:objc_msgSend([MJStudent class], @selector(personClassMethod));
//MJStudent的class对象没有personClassMethod方法,通过superClass指针找到MJPerson的class对象,MJPerson的class对象,通过isa指针找到MJPerson的元类方法,从而找到personClassMethod。从而给[MJStudent class],发送“personClassMethod”消息
}
return 0;
}
-
[stu studentInstanceMethod];
- stu对象不保存对象方法,通过isa指针找到class对象,找到对象方法。
- 这个方法底层实现:objc_msgSend(stu, @selector(studentInstanceMethod));
- C/C++源码: objc_msgSend(stu,Sel_registerName("studentInstanceMethod"));
- 为啥不直接[MJStudent studentInstanceMethod]; ? 语法设计问题,这样不能体现面对对象思想.
- 生成C/C++代码,看上面这部分代码的本质。有NSLog会报错,因为是Foundation框架里的。需要链接Foundation框架。
-
[MJStudent studentClassMethod];
- MJStudent的class对象没有类方法,通过isa指针找到元类的类方法
- 底层实现类似:objc_msgSend([MJStudent class], @selector(studentClassMethod));
-
[MJStudent personClassMethod];
- 底层实现类似:objc_msgSend([MJStudent class], @selector(personClassMethod));
- MJStudent的meta-class对象没有personClassMethod方法,通过superClass指针找到MJPerson的meta-class对象,从而找到personClassMethod。从而给[MJStudent class],发送“personClassMethod”消息。
-
注意:[MJStudent personClassMethod];
MJStudent的class对象,找到MJPerson的class对象,又找到MJPerson的meta-class对象,但是最好并不是MJPerson的meta-class对象调用personClassMethod方法,从而给[MJStudent class],发送“personClassMethod”消息。
-
如果找到基类也没找到方法,会发生什么呢?会抛出经典的错误:unrecognized selector sent to instance
-
[MJStudent abc]如果基类没有+abc方法,会找到基类的-abc方法。(基类的meta-class的superClass指针指向基类的class对象)
实例2
#import <Foundation/Foundation.h>
@interface NSObject (Test)
+ (void)test;
@end
#import "NSObject+Test.h"
@implementation NSObject (Test)
+ (void)test
{
NSLog(@"+[NSObject test] - %p", self);
}
- (void)test
{
NSLog(@"-[NSObject test] - %p", self);
}
@end
@interface MJPerson : NSObject
+ (void)test;
@end
@implementation MJPerson
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"[MJPerson class] - %p", [MJPerson class]);
NSLog(@"[NSObject class] - %p", [NSObject class]);
[MJPerson test];
// objc_msgSend([MJPerson class], @selector(test))
[NSObject test];
// objc_msgSend([NSObject class], @selector(test))
}
return 0;
}
- [MJPerson test];打印结果显示self的地址是[MyPerson class]的地址,虽然调的是NSObject的+test,但是self并不是说一定是NSObject对象。因为是给[MJPerson class]发生消息。
- [Super xxx].涉及到运行时,消息机制。后面runtime再讲。
- 把NSObject的+test方法去掉,换成-test方法。最后发现会调用-test方法。因为本质发送消息,不会区分是类方法还是对象方法。MJPerson的class对象 --> isa --> meta-class --> superClass --> NSObject的meta-class --> superClass --> NSObject的class对象,然后找到-test方法.
2.isa细节
instance的isa指针指向了class对象,那么isa指针存放的地址值就是class对象的地址吗?
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
// MJPerson
@interface MJPerson : NSObject
{
@public
int _age;
}
@end
@implementation MJPerson
@end
// MJStudent
@interface MJStudent : MJPerson
{
@public
int _weight;
}
@end
@implementation MJStudent
@end
struct mj_objc_class {
Class isa;
Class superclass;
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [[MJPerson alloc] init];
struct mj_objc_class *personClass = (__bridge struct mj_objc_class *)([MJPerson class]);
Class personMetaClass = object_getClass((__bridge id _Nullable)(personClass));
NSLog(@"%p %p %p", person, personClass, personMetaClass);
MJStudent *student = [[MJStudent alloc] init];
struct mj_objc_class *studentClass = (__bridge struct mj_objc_class *)([MJStudent class]);
NSLog(@"stu:%p__%p",student,studentClass);
}
return 0;
}
1.上图点击1处右键,选择print可以打印这个对象,但是打印的不是具体内存地址。p (long)person->isa 打印的isa的值(十进制), p/x打印的是16进制。
2.isa指针的值并不是指向的class对象或者meta对象的值。因为64位设备以后,isa指针的值经过,ISA_MASK位运算,即 isa & ISA_MASK为指向的值。
-
ISA_MASK的值在objc源码里有。
-
superClass并不需要位运算,直接就是指向的对象地址值。
class和meta-class的结构
源码:
class和meta-class结构都是一样的,因为都是Class.
点击Class,点进去看一下:
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
里面确实有一些方法列表,缓存,协议列表等,但是有个条件, !OBJC2才会有这个东西,而且OBJC2_UNAVAILABLE,OBJC2后这个结构体已经不可用了。
那么我们来看源码: 源码在objc4里。
搜索struct objc_class.找最新的在objc-runtime-new.h里。(objc-runtime-old里也有)
struct objc_class : objc_object {
// Class ISA; 继承自objc_object
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() {
return bits.data();
}
...
}
- 发现这个结构体继承自objc_object。这是C++代码,C++代码是可以继承的。
- 搜索 “struct objc_object {”
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
...
}
发现objc_object成员变量只有一个isa指针。其他的都是方法。
- class_rw_t 是只读的表。
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro; // ro是readonly
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
...
}
发现里面有calss_ro_t和方法、属性、协议。
class_data_bits_t bits;
class_rw_t *data() {
return bits.data();
}
class_rw_t* data() {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
bits是类的信息,bits与FAST_DATA_MASK进行异或,获得表的内存地址。
- class_ro_t
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#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;
1. 有instanceSize,就是class_getInstanceSize方法获取的成员变量的内存大小。
2. char *name 类名
3. ivar_list_t * ivars。成员变量
总结如下:
窥看在内存中的结构
如果我们要窥看在内存中的结构要怎么做呢?就要造一个与源码struct objc_class类似的结构。
代码如下:
MJClassInfo.h 文件。(不编译MJClassInfo.h)
#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 */
main.mm文件。因为main.m是OC文件,无法识别c++语法,要识别c++语法,必须改成.mm。变成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]);//C++的结构体可以省略 struct
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对象中。
其他问题
1.类对象和元类对象什么时候分配空间。
main函数之后就开始加载类信息,因为分别都只有一份,所以一直在内存中,程序杀死的时候释放。
2.实例对象里的成员变量和类对象成员变量区别。
实例对象里的成员变量放的是值,类对象放的是成员变量的信息。
3.如果父类的一个成员变量是私有的,子类会继承吗?
子类会继承,不过对子类透明。