análisis de la estructura de clases

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.


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_tdel 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。

Supongo que te gusta

Origin blog.csdn.net/weixin_38016552/article/details/125742455
Recomendado
Clasificación