iOS对象创建后在内存里的结构是啥样的?图1
oc的定义结构是啥样的?
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;
Example
假设我们定义一个如下的类
@interface IsaStructClass : NSObject {
NSUInteger _index;
}
@property (nonatomic, copy) NSString *name;
@end
@implementation IsaStructClass
- (void)testPrint {}
+ (void)printClass {}
@end
@interface IsaStructClass (Cate)
@property (nonatomic, copy) NSString *age;
@end
@implementation IsaStructClass (Cate)
- (void)catePrint {}
@end
那么当我们声明一个实例的时候,实际上就是图1里的Instance of root class,我们知道每个类都有一个isa指针,实际上当我们调用一个对象的class方法的时候就是通过调用isa来获取的类名,然后class结构体实例里还有一个isa,这个isa指向他的元类(meta class)
类和元类的区别
我们创建一个 IsaStructClass 的实例,来验证下类与元类的区别:
Method *list = class_copyMethodList([IsaStructClass class], &count);
for (unsigned int i = 0; i<count; i++) {
Method method = list[i];
SEL mthodName = method_getName(method);
NSLog(@"MethodName(%d): %@",i,NSStringFromSelector(mthodName));
}
free(list);
list = class_copyMethodList(objc_getMetaClass("IsaStructClass"), &count);
for (unsigned int i = 0; i<count; i++) {
Method method = list[i];
SEL mthodName = method_getName(method);
NSLog(@"MetaMethodName(%d): %@",i,NSStringFromSelector(mthodName));
}
free(list);
通过上述方法查看类内部的方法及结构,得到结果如下:
2020-07-01 22:21:00.145895+0800 test[68192:3089644] MethodName(0): testPrint
2020-07-01 22:21:00.146004+0800 test[68192:3089644] MethodName(1): catePrint
2020-07-01 22:21:00.146215+0800 test[68192:3089644] MethodName(2): .cxx_destruct
2020-07-01 22:21:00.146486+0800 test[68192:3089644] MethodName(3): name
2020-07-01 22:21:00.146840+0800 test[68192:3089644] MethodName(4): setName:
2020-07-01 22:21:00.147307+0800 test[68192:3089644] MetaMethodName(0): printClass
可以看到,类内存储的全部是非class方法,而元类存储的是类方法
内存结构
通过上边的输出,我们可以得出一个结论,子类的方法列表是不包含父类的方法列表的,因为你可以看到输出里面并没有任何他的父类的信息
由此我们可以推断,当我们调用一个对象的方法的时候,实际上会调用objc_msgSend(void /* id self, SEL op, ... */ )
,可以推断方法内部的实现逻辑大概是:
获取当前类方法列表,如果响应则send,没有则找到父类看是否响应,以此类推,都不响应则调用基类的消息转发;如果是class方法则走元类的方法
属性 & 变量
unsigned int count;
objc_property_t *propertyList = class_copyPropertyList([IsaStructClass class], &count);
for (unsigned int i = 0; i<count; i++) {
const char *propertyName = property_getName(propertyList[i]);
NSLog(@"PropertyName(%d): %@",i,[NSString stringWithUTF8String:propertyName]);
}
free(propertyList);
Ivar *ivarList = class_copyIvarList([IsaStructClass class], &count);
for (int i= 0; i<count; i++) {
Ivar ivar = ivarList[i];
const char *ivarName = ivar_getName(ivar);
NSLog(@"Ivar(%d): %@", i, [NSString stringWithUTF8String:ivarName]);
}
free(ivarList);
查看内存结构输出如下:
2020-07-01 22:21:00.145134+0800 test[68192:3089644] PropertyName(0): age
2020-07-01 22:21:00.145253+0800 test[68192:3089644] PropertyName(1): name
2020-07-01 22:21:00.145553+0800 test[68192:3089644] Ivar(0): _index
2020-07-01 22:21:00.145669+0800 test[68192:3089644] Ivar(1): _name
得出结论如下:
1.直接声明的var不会增加property列表,ivar列表是类内所有用于存储数据的集合
2.分类内声明一个property是不会增加ivar的,因为他只是声明一个property,甚至不会新增property的方法,要实现分类内增加property,需要自己实现方法,并用关联对象来实现。
3.关联对象的存储是在字典里,并不会增加ivar
动态增加方法
Method method = class_getInstanceMethod([self class], @selector(print3));
class_addMethod([IsaStructClass class], @selector(print3), method_getImplementation(method), method_getTypeEncoding(method));
运行时增加方法是动态修改的methodlist,所以获取的method里会有这个方法