建设游戏网站需要什么设备,黄石做网站公司,电影网站建设视频教程,国内网页加速器目录 对象的分类object_getClass和class方法isa流程和继承链分析isa流程实例验证类的继承链实例验证 类的结构cache_t结构bits分析实例验证属性properties方法methods协议protocolsro类方法 类结构流程图解 对象的分类
OC中的对象主要可以分为3种#xff1a;实例对象#xf… 目录 对象的分类object_getClass和class方法isa流程和继承链分析isa流程实例验证类的继承链实例验证 类的结构cache_t结构bits分析实例验证属性properties方法methods协议protocolsro类方法 类结构流程图解 对象的分类
OC中的对象主要可以分为3种实例对象instance、类对象class和元类对象meta-class
实例对象
通过类alloc出来的对象每次调用alloc都会产生新的instance对象
NSObject* obj1 [[NSObject alloc] init];
NSObject* obj2 [[NSObject alloc] init];
NSLog(%p %p, obj1, obj2);
// 打印结果0x600000180040 0x600000180050从运行结果可看出以上是不同的两个实例对象分别占据着两块不同的内存 实例对象在内存中存储的信息包括isa指针、其他成员变量
类对象
#import objc/runtime.h
Class objectClass1 [obj1 class];
Class objectClass2 [obj2 class];
Class objectClass3 [NSObject class];
Class objectClass4 object_getClass(obj1); //Runtime API
Class objectClass5 object_getClass(obj2); //Runtime API
// 打印结果0x1d6fc6070 0x1d6fc6070 0x1d6fc6070 0x1d6fc6070 0x1d6fc6070以上都是NSObject的类对象从运行结果可看出它们都是同一个对象即这些指针指向的是同一块内存每个类在内存中有且只有一个class对象 类对象在内存中存储的信息主要包括isa指针、superclass指针、类的属性信息property、类的对象方法信息instance method、类的协议信息protocol、类的成员变量ivar类型、名称等描述信息而不是具体的值
元类对象
看下面如何获取元类对象元类对象类型仍是一个类对象底层都是struct objc_class* Class只是包含的信息不一样
Class objectMetaClass object_getClass(object_getClass(obj1));将类对象作为参数传入再次调用object_getClass函数
那如果调用两次class方法呢
Class objectMetaClass2 [[NSObject class] class];
NSLog(%p %p %d, objectMetaClass, objectMetaClass2, class_isMetaClass(objectMetaClass));
// 打印结果0x1d6fc6020 0x1d6fc6070 1 0从打印结果可以看出class不管调多少次返回的一直是类对象不会是元类对象 每个类只有一个元类对象元类对象在内存中存储的信息主要包括isa指针、superclass指针以及类方法信息
object_getClass和class方法
查看objc4源码
object_getClass方法中传入各种对象通过访问isa返回不同的类对象
Class object_getClass(id obj)
{if (obj) return obj-getIsa();else return Nil;
}// 传入类名字符串返回对象的类对象
Class objc_getClass(const char *aClassName)
{if (!aClassName) return Nil;// NO unconnected, YES class handlerreturn look_up_class(aClassName, NO, YES);
}class方法直接返回类对象
// (id)self {
// return (id)self;
//}
//- (id)self {
// return self;
//} (Class)class {return self;
}- (Class)class {return object_getClass(self);
}// (Class)superclass {
// return self-getSuperclass();
//}
//- (Class)superclass {
// return [self class]-getSuperclass();
//}isa流程和继承链分析
上面我们了解了对象的分类认识到不同类型对象的差别那么是什么让这些不同类型的对象联系起来从而构成OC对象体系的呢
上经典老图 isa指向链
实际上就是isa指针将它们联系起来形成 isa指向链
实例对象instance的isa指向类class类对象class也有isa指向的是元类meta元类meta中也有isa指向的是根元类root meta 当调用对象方法时通过实例对象的isa找到class最后找到对象方法的实现进行调用 当调用类方法时通过类对象的isa找到meta-class最后找到类方法的实现进行调用
类继承链
根据superclass的指向也可总结出OC类的继承链
子类继承于父类父类继承于根类根类指向的是nil在元类中也存在继承子类的元类继承于父类的元类父类的元类继承于根元类根元类又继承与根类 当Student的实例对象要调用Person的对象方法时会先通过isa找到Student的class然后通过superclass找到Person的class最后找到对象方法的实现进行调用
类似地当Student的类对象要调用Person的类方法时会先通过isa找到Student的meta-class然后通过superclass找到Person的meta-class最后找到类方法的实现进行调用
isa流程实例验证
Person类继承于NSObjectStudent类继承于Person
interface Person : NSObject {publicint _age;
}- (void)personInstanceMethod;(void)personClassMethod;endinterface Student : Person {publicint _no;
}- (void)studentInstanceMethod;(void)studentClassMethod;end打断点通过LLDB查看isa关联类的地址
// 打印出实例的地址
Person* person [Person alloc];
NSLog(%, person);
Student* student [Student alloc];
NSLog(%, student);类对象的地址和实例对象isa所指向的地址有所出入isa需要进行一次位运算才能计算出类对象的真实地址 在获取到对象的isa值后可以通过按位与一个掩码ISA_MASK 0x007ffffffffffff8ULL来获取到对象关联的类地址 根据student实例的isa地址找到关联类Student的地址0x00000001000082d8 同样地根据Student类对象的isa找到Student元类的地址0x00000001000082b0 根据Student元类对象的isa找到关联类的地址0x00000001d6fc6020 找到NSObject类对象的isa关联类地址0x00000001d6fc6020与Student元类对象的isa关联类地址一致可以验证元类的isa指向根元类且根元类的isa指向自己 类的继承链实例验证
Class tClass [Student class];
Class pClass class_getSuperclass(tClass);
Class nClass class_getSuperclass(pClass);
Class rClass class_getSuperclass(nClass);
NSLog(\n tClass-% \n pClass-% \n nClass-% \n rClass-% \n, tClass, pClass, nClass, rClass);可看出类对象的继承链Student-Person-NSObject-nil
Student * student [Student alloc];
Class tClass object_getClass(student);
Class mtClass object_getClass(tClass);
Class mtSuperClass class_getSuperclass(mtClass);
NSLog(\n student %p 实例对象 -- %p 类 -- %p 元类 -- %p 元类父类, student, tClass, mtClass, mtSuperClass);
Person * person [Person alloc];
Class pClass object_getClass(person);
Class mpClass object_getClass(pClass);
Class mpSuperClass class_getSuperclass(mpClass);
NSLog(\n person %p 实例对象 -- %p 类 -- %p 元类 -- %p 元类父类, person, pClass, mpClass, mpSuperClass);
NSObject * obj [NSObject alloc];
Class objClass object_getClass(obj);
Class mobjClass object_getClass(objClass);
Class mobjSuperClass class_getSuperclass(mobjClass);
NSLog(\n NSObject %p 实例对象 -- %p 类 -- %p 元类 -- %p 元类父类 %p NSObject类对象, obj, objClass, mobjClass, mobjSuperClass,
[NSObject class]);可看出元类的继承链Student Meta-class - Person Meta-class - NSObject Meta-class - NSObject class - nil
类的结构
前面我们了解到了Class的类型是struct objc_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 vtableclass_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags// ...其他代码objc_class定义共计531行代码...
};继承于objc_object说明
还有一个继承过来的Class类型变量isasuperclass指向父类的指针cache缓存相关bits用于获取具体的类信息
cache_t结构
cache_t是一个结构体
struct cache_t {
private:explicit_atomicuintptr_t _bucketsAndMaybeMask; // 8字节union {struct {explicit_atomicmask_t _maybeMask; // uint32_t 4字节
#if __LP64__uint16_t _flags; // 2字节
#endifuint16_t _occupied; // 2字节};explicit_atomicpreopt_cache_t * _originalPreoptCache; // 8字节};};
// 此段为部分代码cache_t定义总共有290行分析整个cache_t的结构发现cache_t的内存总共为16字节后面会对其底层进行学习
bits分析
在objc_class里有一段源码是data操作
class_rw_t *data() const {return bits.data();
}
void setData(class_rw_t *newData) {bits.setData(newData);
}data为class_rw_t类型下面是其部分源码 ro成员变量、methods方法、properties属性、protocols协议 我们在类中定义的方法、属性等就是通过调取class_rw_t结构体中的方法获取的
实例验证
下面通过实例来验证一下类的结构是否如上面一致 创建Person类继承于NSObject定义一些属性、方法以及协议
protocol PersonDelegateNSObject- (void)personDelegateMethod;
// 让Person类遵守并实现此协议方法
endinterface Person : NSObjectPersonDelegate {NSString* hobby;
}property (nonatomic, strong)NSString* name;
property (nonatomic, assign)NSInteger age;- (void)sayHello;(void)sayWorld;endLLDB调试输出 第一个地址0x0000000100008470是类的第一个成员isa第二个地址0x00000001d6fc6070是类的第二个成员superclass isa和superclass都是结构体指针类型占用8字节cache结构体占用16字节XYPerson的地址加上8 8 16 32就可以得到bits的地址 相加并强转为class_data_bits_t *类型得到bits的地址0x0000000100008270再调用data()方法就得到类型为class_rw_t的地址
属性properties
调用class_rw_t的properties()方法得到property_array_t类型的数组继承于list_array_tt找到list下的ptr class property_array_t :public list_array_ttproperty_t, property_list_t, RawPtr
{typedef list_array_ttproperty_t, property_list_t, RawPtr Super;public:property_array_t() : Super() { }property_array_t(property_list_t *l) : Super(l) { }
};ptr为property_list_t类型继承于entsize_list_tt
struct property_list_t : entsize_list_ttproperty_t, property_list_t, 0 {
};entsize_list_tt部分源码
struct entsize_list_tt {uint32_t entsizeAndFlags;uint32_t count; // 数量uint32_t entsize() const {return entsizeAndFlags ~FlagMask;}uint32_t flags() const {return entsizeAndFlags FlagMask;}Element getOrEnd(uint32_t i) const {ASSERT(i count);return *PointerModifier::modify(*(List *)this, (Element *)((uint8_t *)this sizeof(*this) i*entsize()));}Element get(uint32_t i) const { // 获取元素方法ASSERT(i count);return getOrEnd(i);}// ...其他代码...
};通过调用get()方法获取元素下面的结果就是Person类的name、age在properties()里而实例变量hobby不在这里 方法methods
调用class_rw_t的methods()方法得到method_array_t类型的数组继承于list_array_tt同样找到list下的ptr 这里看到ptr是method_list_t类型同样继承于entsize_list_tt其中有count为6调用get()方法查看输出 这里的元素为method_t类型method_t为结构体类型其中的一个成员变量为big的结构体里面是方法名称等信息
struct method_t {method_t(const method_t other) delete;// The representation of a big method. This is the traditional// representation of three pointers storing the selector, types// and implementation.struct big {SEL name;const char *types;MethodListIMP imp;};
// ...其他代码
};调用big方法查看输出 这6个方法分别是
实例方法sayHello属性name、age的set/get方法C析构函数.cxx_destruct
且都是实例方法并没有类方法sayWorld
协议protocols
调用class_rw_t的protocols()方法得到protocol_array_t类型的数组继承于list_array_tt同样找到list下的ptr 这里protocol_list_t并没有继承于entsize_list_tt
struct protocol_list_t {// count is pointer-sized by accident.uintptr_t count;protocol_ref_t list[0]; // variable-sizesize_t byteSize() const {return sizeof(*this) count*sizeof(list[0]);}protocol_list_t *duplicate() const {return (protocol_list_t *)memdup(this, this-byteSize());}typedef protocol_ref_t* iterator;typedef const protocol_ref_t* const_iterator;const_iterator begin() const {return list;}iterator begin() {return list;}const_iterator end() const {return list count;}iterator end() {return list count;}
};看到protocol_list_t的定义我们知道count值为1说明是有值但是其成员是protocol_ref_t为uintptr_t类型那怎么输出查看这个count中的1到底是什么呢 查看protocol_ref_t的定义通过注释信息我们可以看到protocol_ref_t未映射到protocol_t类型那我们就找protocol_t的定义 这里看到protocol_t中有mangledName以及instanceMethods等只要得到protocol_t就可以输出我们想要的名称方法等信息怎么才能从protocol_ref_t映射到protocol_t呢全局找一下吧 这里我们看到protocol_ref_t是可以强转protocol_t的那我们就试试 强转成功调用demangledName方法我们就得到了LGPersonDelegate那我们再找一下协议方法 按照method查看输出的步骤成功找到协议方法personDelegateMethod
ro
调用class_rw_t的ro方法得到class_ro_t的结构体 查看ivars也是继承于entsize_list_tt的ivar_list_t类型的结构体调用get方法查看 这6个实例变量分别是自定义hobby以及系统自动帮我们自动生成的带有_的实例变量
类方法
methods中的方法全部都存在类中都是实例方法那么类方法应该去在元类中找 通过类的isa指针找到元类再根据上面的步骤找到并输出这个元类的methods 这里我们不由地想OC的底层是C/C实现的不存在对象方法和类方法的区分有的都是函数实现在OC的设计中一个类可以new出无数个对象因此把方法存在类中而不是动态创建的对象中是合理的。 因为OC的对象方法和类方法的定义是-和的区分那么方法名称就会有重名的存在因此才会引入元类的概念元类的存在就是解决类方法重名的问题 类结构流程图解
类的结构流程图解析