El código fuente de objc en este artículo es objc4-838, y los diferentes códigos fuente pueden variar del sitio web oficial de Apple.
Directorio de artículos
Tabla de contenido
2. Resumen de
3. es un gráfico apuntador y un gráfico de superclase
4. Análisis y exploración de la estructura de clases
1. código fuente objc_object y objc_class
a. Obtenga la primera dirección de class_data_bits_t bits
b. Obtenga la dirección de datos del tipo class_rw_t
c. Ver las propiedades de la lista de propiedades
prefacio
本文主要探究类的结构并分析类方法及ivar
Uno, isa apunta a
Primero personaliza una clase SLPerson.
@interface SLPerson : NSObject
{
NSString * subject;
}
@property(copy,nonatomic)NSString * name;
@property(copy,nonatomic)NSString * hobby;
-(void)sayNB;
+(void)say666;
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
SLPerson * p = [[SLPerson alloc];
NSObject * obj = [NSObject alloc];
}
return 0;
}
Use lldb para ver la dirección de memoria y analícela como se muestra en la siguiente figura
El isa del objeto de instancia SLPerson p apunta a: p—>objeto de clase SLPerson—>objeto de metaclase SLPerson—>objeto de metaclase NSObject—>objeto de metaclase NSObject (apuntando a sí mismo).
2. Resumen de la relación de herencia de la superclase
1. No existe una relación de herencia entre los objetos de instancia , solo las clases o metaclases tienen una relación de herencia.
2. La metaclase NSObject hereda la clase NSObjct y la clase NSObjct hereda de nil
3. es un gráfico apuntador y un gráfico de superclase
4. Análisis y exploración de la estructura de clases
1. código fuente objc_object y objc_class
typedef struct objc_class *Class;
typedef struct objc_object *id;
struct objc_class : objc_object {
.....
// Class ISA; //8字节 从objc_object继承而来
Class superclass; //8字节
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();
}
}
La información de la clase está en class_data_bits_t , y la memoria miembro en struct objc_class es continua, por lo que el código fuente se puede ver a través de la dirección de clase más la traducción de memoria de puntero específica . isa y superclass ocupan 8 bytes, analicemos el tamaño de cache_t.
tamaño de caché_t
Primero observe el código fuente, dado que la variable estática no ocupa el espacio de almacenamiento de esta estructura, analizamos el código clave de la siguiente manera:
struct cache_t {
private:
explicit_atomic<uintptr_t> _bucketsAndMaybeMask; //8
union {
struct {
explicit_atomic<mask_t> _maybeMask; //4
#if __LP64__
uint16_t _flags; //2
#endif
uint16_t _occupied; //2
};
explicit_atomic<preopt_cache_t *> _originalPreoptCache; //8
};
......此处省略好多代码
}
Se puede concluir que el tamaño de la unión es de 8 bytes, más los 8 bytes de _bucketsAndMaybeMask , el tamaño de cache_t es de 16 bytes. Entonces podemos obtener la primera dirección de class_data_bits_t bits agregando 8+8+16=32 bytes (0x20) de compensación a la primera dirección de la clase.
2.class_data_bits_t
a. Obtengala primera dirección de class_data_bits_t bits
(lldb) x/4gx SLPerson.class
0x100008388: 0x0000000100008360 0x000000010080e140
0x100008398: 0x0000000100806230 0x0000802800000000
(lldb) p (class_data_bits_t *)(0x100008388+0x20)
(class_data_bits_t *) $1 = 0x00000001000083a8
b. Obtener la dirección class_rw_t
del tipo data
(lldb) p $1->data()
(class_rw_t *) $2 = 0x0000000100f047e0
(lldb) p *$2
(class_rw_t) $3 = {
flags = 2148007936
witness = 0
ro_or_rw_ext = {
std::__1::atomic<unsigned long> = {
Value = 4295000488
}
}
firstSubclass = nil
nextSiblingClass = 0x00007ff8480b19c8
}
C. Ver lista de propiedadesproperties
(lldb) p $3.properties()
(const property_array_t) $4 = {
list_array_tt<property_t, property_list_t, RawPtr> = {
= {
list = {
ptr = 0x0000000100008180
}
arrayAndFlag = 4295000448
}
}
}
(lldb) p $4.list
(const RawPtr<property_list_t>) $5 = {
ptr = 0x0000000100008180
}
(lldb) p $5.ptr
(property_list_t *const) $6 = 0x0000000100008180
(lldb) p *$6
(property_list_t) $7 = {
entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2)
}
(lldb) p $7.get(0)
(property_t) $8 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
(lldb) p $7.get(1)
(property_t) $9 = (name = "hobby", attributes = "T@\"NSString\",C,N,V_hobby")
Resumen: puede encontrar los atributos name y hobby llamando a properties, pero no puede encontrar el asunto de la variable miembro .
D. Ver métodos ()
(lldb) p $10.list.ptr
(method_list_t *const) $11 = 0x0000000100008098
(lldb) p *$11
(method_list_t) $12 = {
entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 5)
}
(lldb) p $12.get(0).big()
(method_t::big) $14 = {
name = "sayNB"
types = 0x0000000100003ede "v16@0:8"
imp = 0x0000000100003be0 (KCObjcBuild`-[SLPerson sayNB])
}
(lldb) p $12.get(1).big()
(method_t::big) $15 = {
name = "hobby"
types = 0x0000000100003ee6 "@16@0:8"
imp = 0x0000000100003c40 (KCObjcBuild`-[SLPerson hobby])
}
(lldb) p $12.get(2).big()
(method_t::big) $16 = {
name = "setHobby:"
types = 0x0000000100003eee "v24@0:8@16"
imp = 0x0000000100003c60 (KCObjcBuild`-[SLPerson setHobby:])
}
(lldb) p $12.get(3).big()
(method_t::big) $17 = {
name = "name"
types = 0x0000000100003ee6 "@16@0:8"
imp = 0x0000000100003bf0 (KCObjcBuild`-[SLPerson name])
}
(lldb) p $12.get(4).big()
(method_t::big) $18 = {
name = "setName:"
types = 0x0000000100003eee "v24@0:8@16"
imp = 0x0000000100003c10 (KCObjcBuild`-[SLPerson setName:])
}
Resumen: llamar methods()
al método impreso no tiene métodos de clase, solo métodos de instancia .
3. Ver ivares
(lldb) x/4gx SLPerson.class
0x100008388: 0x0000000100008360 0x000000010080e140
0x100008398: 0x0000000100806230 0x0000802800000000
(lldb) p (class_data_bits_t *)(0x100008388+0x20)
(class_data_bits_t *) $1 = 0x00000001000083a8
(lldb) p $1->data()
(class_rw_t *) $2 = 0x0000000100f12330
(lldb) p *$2
(class_rw_t) $3 = {
flags = 2148007936
witness = 0
ro_or_rw_ext = {
std::__1::atomic<unsigned long> = {
Value = 4295000488
}
}
firstSubclass = nil
nextSiblingClass = 0x00007ff8480b19c8
}
(lldb) p $3.ro()
(const class_ro_t *) $4 = 0x00000001000081a8
(lldb) p *$4
(const class_ro_t) $5 = {
flags = 0
instanceStart = 8
instanceSize = 32
reserved = 0
= {
ivarLayout = 0x0000000000000000
nonMetaclass = nil
}
name = {
std::__1::atomic<const char *> = "SLPerson" {
Value = 0x0000000100003e08 "SLPerson"
}
}
baseMethods = {
ptr = 0x0000000100008098
}
baseProtocols = nil
ivars = 0x0000000100008118
weakIvarLayout = 0x0000000000000000
baseProperties = 0x0000000100008180
_swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $5.ivars
(const ivar_list_t *const) $6 = 0x0000000100008118
(lldb) p *$6
(const ivar_list_t) $7 = {
entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 3)
}
(lldb) p $7.get(0)
(ivar_t) $8 = {
offset = 0x0000000100008348
name = 0x0000000100003e45 "subject"
type = 0x0000000100003ef9 "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $7.get(1)
(ivar_t) $9 = {
offset = 0x0000000100008350
name = 0x0000000100003e4d "_name"
type = 0x0000000100003ef9 "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $7.get(2)
(ivar_t) $10 = {
offset = 0x0000000100008358
name = 0x0000000100003e53 "_hobby"
type = 0x0000000100003ef9 "@\"NSString\""
alignment_raw = 3
size = 8
}
Resumen: en ivar_list_t, los atributos (nombre, pasatiempo) estarán subrayados y las variables de miembro (sujeto) no estarán subrayadas.
4. Método de clase
Resumen: Los métodos de clase existen en objetos de metaclase .
5. esKindOfClass y esMiembroDeLaClase
1.esKindOfClass:
El código fuente es el siguiente:
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
Código de prueba:
int main(int argc, const char * argv[]) {
@autoreleasepool {
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; //
BOOL re2 = [(id)[SLPerson class] isKindOfClass:[SLPerson class]]; //
BOOL re3 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]]; //
BOOL re4 = [(id)[SLPerson alloc] isKindOfClass:[SLPerson class]]; //
NSLog(@"\n re1:%hhd re2:%hhd re3:%hhd re4:%hhd",re1,re2,re3,re4);
}
return 0;
}
resultado:
- re1——1.tcls = self->ISA() es la metaclase NSObject (es decir, la metaclase raíz ), cls es [clase NSObject] es la clase 2. tcls es tcls->superclase, y la clase padre de la metaclase raíz es la clase NSObject Igual a cls, devuelve SÍ.
- re2——1.tcls = self->ISA() es la metaclase SLPerson , cls es [SLPerson class] es una clase , 2. tcls es tcls->superclass, y la clase principal de la metaclase SLPerson es la metaclase raíz , que no es igual a cls 3. La clase principal de la metaclase raíz es la clase NSObject , que no es igual a cls 4. La clase principal de la clase NSObject es nula, el ciclo for termina y devuelve NO.
- re3 - tcls = [self class] es la clase NSObject , igual a cls, devuelve SÍ.
- re4——tcls = [self class] es la clase , igual a cls, devuelve SÍ.
2.esMiembroDeLaClase
El código fuente es el siguiente:
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
Código de prueba:
int main(int argc, const char * argv[]) {
@autoreleasepool {
BOOL re5 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; //
BOOL re6 = [(id)[SLPerson class] isMemberOfClass:[SLPerson class]]; //
BOOL re7 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]]; //
BOOL re8 = [(id)[SLPerson alloc] isMemberOfClass:[SLPerson class]]; //
NSLog(@"\n re5:%hhd re6:%hhd re7:%hhd re8:%hhd",re5,re6,re7,re8);
}
return 0;
}
resultado
- re5 -
self->ISA()
si根元类,cls是NSObject类,不相等返回NO。
re6——self->ISA()
es la metaclase SLPerson, cls es la clase SLPerson y devuelve NO si no son iguales.- re7——
self->ISA()是NSObject类,等于cls,返回YES。
- re8——
self->ISA()是
Persona SL类,等于cls,返回YES。