Puntero iOS-isa; objeto de instancia objc, clase, metaclase, clase raíz, metaclase raíz

1. Objeto (objeto de instancia objc), Clase (clase), Metaclase (metaclase), Rootclass (clase raíz), Metaclase de Rootclass (metaclase raíz)

Para entender el puntero isa en iOS, no podemos prescindir de varias estructuras de datos de clases en Objective-C; en la estructura de tipo de Objective-C, Object (instancia), Class (clase), Metaclass (metaclase), Rootclass (clase raíz ), la metaclase de Rootclass (metaclase raíz), y estos son objetos.

Eche un vistazo al diagrama del modelo de objetos de Objective-C para ayudar a comprender:

Objective-C---->C/C++----->lenguaje ensamblador---->lenguaje de máquina; sabemos que Objective-C es un lenguaje orientado a objetos, y todos sus objetos son instanciados por sus clases correspondientes De hecho, la clase misma es también un objeto;

En Objective-C, casi todas las clases que usamos son subclases de la clase NSObject 

Abrimos el archivo NSObject.h en el archivo de cabecera #import <objc/runtime.h> en Xcode o código fuente abierto de Apple

( Navegación en línea del código fuente objc4 de código abierto oficial de Apple ; descarga del código fuente objc4 , la versión objc4-818.2 utilizada en este artículo)

El formato de definición de la clase NSObject es el siguiente (ignorando su declaración de método):

@interface NSObject <NSObject> {
    Class isa;
}

Abra el archivo objc.h y encontrará que la clase (CLass) y el objeto de instancia (objc o instancia) se definen de la siguiente manera:

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class; //class类对象

/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id; //objc实例对象

Class es un puntero a la estructura objc_class (clase) , e id es un puntero a la estructura objc_object (objeto de instancia).

La estructura de clase a la que apunta el puntero isa en objc_object (objeto de instancia) se denomina objc_class (la clase del objeto), que almacena variables miembro ordinarias y métodos de objeto (métodos que comienzan con "-") .

La estructura de clase a la que apunta el puntero isa en objc_class (clase) se llama metaclase (metaclase de la clase), que almacena variables miembro de tipo estático y métodos de tipo estático (métodos que comienzan con "+") .

/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

La estructura objc_object (objeto de instancia) tiene solo un atributo de miembro de isa, que apunta a objc_class (la clase del objeto).

en el archivo runtime.h

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;   //isa指针,指向metaclass(该类的元类)

#if !__OBJC2__

    Class super_class   //指向objc_class(该类)的super_class(父类)

    const char *name    //objc_class(该类)的类名

    long version        //objc_class(该类)的版本信息,初始化为0,可以通过runtime函数class_setVersion和class_getVersion进行修改和读取

    long info           //一些标识信息,如CLS_CLASS表示objc_class(该类)为普通类。ClS_CLASS表示objc_class(该类)为metaclass(元类)

    long instance_size  //objc_class(该类)的实例变量的大小

    struct objc_ivar_list *ivars    //用于存储每个成员变量的地址

    struct objc_method_list **methodLists   //方法列表,与info标识关联

    struct objc_cache *cache        //指向最近使用的方法的指针,用于提升效率

    struct objc_protocol_list *protocols    //存储objc_class(该类)的一些协议
#endif

} OBJC2_UNAVAILABLE;

objc_class (clase) tiene muchos más miembros que la estructura objc_object (objeto de instancia) , y la función de cada miembro se presenta en la nota.

Por lo tanto, encontraremos objetos de instancia de objc, objetos de clase, metaclases, clases raíz, metaclases raíz

1. Objeto de instancia (Object): Un objeto u objc de instancia que creamos es en realidad una estructura struct objc_object, que tiene solo una variable miembro, que es una variable isa de tipo Clase, y también es un puntero de estructura. el puntero apunta a su objeto de clase (es decir, la clase que solemos llamar)

La estructura de diseño de los objetos objc en la memoria es la siguiente:

Diagrama estructural de un objeto Objective-C
puntero ISA
Variables de instancia de la clase raíz
Variables de instancia de la penúltima clase padre
...
Variables de instancia de la clase padre
variables de instancia de clase

Clase (CLass): almacena datos relacionados de instancias de objetos, como: lista de métodos de instancia, lista de variables miembro, lista de atributos.

Metaclase (Metaclass): almacene datos relacionados con la clase, como: lista de métodos de clase, información de clase, etc.

La ubicación de isa y su función_la primera imagen

二、isa指针

Miremos de nuevo el código fuente del archivo NSObject.mm y veamos el método de instancia - (CLass)class

// 类方法,返回自身
+ (Class)class {
    return self;
}
 
// 实例方法,查找isa(类)
- (Class)class {
    return object_getClass(self);
}

Creamos un objeto de instancia objc, llamamos a la clase (CLass), en realidad llamamos al método object_getClass

Hagamos clic en el método object_getClass para ver la definición del código fuente, en el archivo objc-class.mm

/***********************************************************************
* object_getClass.
* Locking: None. If you add locking, tell gdb (rdar://7516456).
**********************************************************************/
Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

Continúe haciendo clic en el método getIsa() en el archivo objc-object.h

inline Class
objc_object::getIsa() 
{
    if (fastpath(!isTaggedPointer())) return ISA();

    extern objc_class OBJC_CLASS_$___NSUnrecognizedTaggedPointer;
    uintptr_t slot, ptr = (uintptr_t)this;
    Class cls;

    slot = (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
    cls = objc_tag_classes[slot];
    if (slowpath(cls == (Class)&OBJC_CLASS_$___NSUnrecognizedTaggedPointer)) {
        slot = (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
        cls = objc_tag_ext_classes[slot];
    }
    return cls;
}

que se puede simplificar a

inline Class
objc_object::getIsa() 
{
    if (!isTaggedPointer()) return ISA();
    return cls;
}

 Continúe y haga clic ISA()方法,en el archivo objc-object.h

En  la versión objc4-787.1 de código abierto oficial de Apple

#if SUPPORT_NONPOINTER_ISA

inline Class 
objc_object::ISA() 
{
    ASSERT(!isTaggedPointer()); 
#if SUPPORT_INDEXED_ISA
    if (isa.nonpointer) {
        uintptr_t slot = isa.indexcls;
        return classForIndex((unsigned)slot);
    }
    return (Class)isa.bits;
#else
    return (Class)(isa.bits & ISA_MASK);
#endif
}

En  la versión objc4-818.2 oficial de código abierto de Apple

//objc-object.h文件
inline Class
objc_object::ISA(bool authenticated)
{
    ASSERT(!isTaggedPointer());
    return isa.getDecodedClass(authenticated);
}


//objc-object.h文件
inline Class
isa_t::getDecodedClass(bool authenticated) {
#if SUPPORT_INDEXED_ISA
    if (nonpointer) {
        return classForIndex(indexcls);
    }
    return (Class)cls;
#else
    return getClass(authenticated);
#endif
}


//objc-object.h文件
inline Class
isa_t::getClass(MAYBE_UNUSED_AUTHENTICATED_PARAM bool authenticated) {
#if SUPPORT_INDEXED_ISA
    return cls;
#else

    uintptr_t clsbits = bits;

#   if __has_feature(ptrauth_calls)
#       if ISA_SIGNING_AUTH_MODE == ISA_SIGNING_AUTH
    // Most callers aren't security critical, so skip the
    // authentication unless they ask for it. Message sending and
    // cache filling are protected by the auth code in msgSend.
    if (authenticated) {
        // Mask off all bits besides the class pointer and signature.
        clsbits &= ISA_MASK;
        if (clsbits == 0)
            return Nil;
        clsbits = (uintptr_t)ptrauth_auth_data((void *)clsbits, ISA_SIGNING_KEY, ptrauth_blend_discriminator(this, ISA_SIGNING_DISCRIMINATOR));
    } else {
        // If not authenticating, strip using the precomputed class mask.
        clsbits &= objc_debug_isa_class_mask;
    }
#       else
    // If not authenticating, strip using the precomputed class mask.
    clsbits &= objc_debug_isa_class_mask;
#       endif

#   else
    clsbits &= ISA_MASK;
#   endif

    return (Class)clsbits;
#endif
}

Todos podemos simplificar esto a

inline Class 
objc_object::ISA() 
{
    return (Class)(isa.bits & ISA_MASK);
}

Volviendo al origen, vemos que el método de instancia  - clase (CLass) finalmente obtiene: la estructuraobjc_objectisa.bits & ISA_MASK

接下来解释一下

1、inline关键字作用
2、在方法objc_object::ISA() 中双冒号::的作用
3、objc_object中的isa
4、isa.bits & ISA_MASK

1、inline关键字

La función en línea utilizada para definir una clase, la razón principal para introducirla es usarla para reemplazar la definición de macro en la forma de expresión en C.

Las funciones en línea se modifican con inlinepalabras clave, y las funciones en línea se usan para reemplazar las definiciones de macro. La razón para reemplazar la definición de macro es:

(1) La razón para usar la forma de definir en C es porque el lenguaje C es un lenguaje muy eficiente. Este tipo de definición de macro es como una función en forma y uso, pero es implementada por el preprocesador, y no hay A serie de operaciones como la inserción de parámetros y la generación de código son muy eficientes, que es una de las razones principales por las que se usa en C.
(2) Este tipo de definición de macro es similar a una función en forma, pero cuando se usa, es solo un reemplazo simple en la tabla de símbolos del preprocesador, por lo que no puede verificar la validez de los parámetros y no puede disfrutar de la compilación de C++. , su valor de retorno no se puede forzar a convertir a un tipo adecuado que se pueda convertir, por lo que su uso tiene una serie de peligros y limitaciones ocultos.
(3) Clase y control de acceso a clases se introducen en C++, de modo que si una operación o expresión involucra un miembro protegido o un miembro privado de una clase, no puede usar este tipo de definición de macro para lograrlo (porque no puede ser este puntero en su lugar).
(4) El propósito de en línea es reemplazar la definición de macro en forma de expresión. Elimina las deficiencias de la definición de macro y, al mismo tiempo, hereda bien las ventajas de la definición de macro.

2、方法objc_object::ISA() 中双冒号::

双冒号::Se utiliza para representar un "operador de dominio".Por ejemplo: se declara una clase A y se declara una función miembro void f() en la clase A, pero la definición de f no se da en la declaración de clase, entonces cuando f se define fuera de la clase, debe escribirse como void A::f(), lo que indica que la función f() es una función miembro de la clase A.

3、objc_object中的isa指针

根据objc_objectLa definición de isa, podemos saber que isa es en realidad un isa_tobjeto, continúe para ver isa_tla implementación:

union isa_t 
{
//这里省略很多变量
}

isa_tEs una unión, es decir: objc_object de isahecho es una estructura 

4、isa.bits & ISA_MASK

isaEs una unión, y sus bits de atributo interno

union isa_t 
{
   //省略部分方法和属性...
    uintptr_t bits;
}

Luego mira uintptr_tla implementación:

typedef unsigned long       uintptr_t;

Encontró que es un unsigned longtipo, ISA_MASKla definición:

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
# else
#   error unknown architecture for packed isa
# endif

De hecho, ISA_MASKes un tipo numérico. Es decir, juzgar si dos objetos son iguales classse objc_objectobtiene comparando los resultados de los cálculos numéricos en la comparación.

3. La relación entre el puntero isa y el objeto (objeto de instancia de objc), clase (clase), metaclase (metaclase), clase raíz (clase raíz), metaclase de clase raíz (metaclase raíz)

1. Subclase, código de prueba de objeto de instancia de clase principal:

    //继承关系: Man : People : NSObject(Man继承自People继承自NSObject)
    NSObject *object_instance = [[NSObject alloc]init];
    People *people = [[People alloc]init];
    Man *man = [[Man alloc]init];
    
    NSLog(@"object_instance----     %p" , object_instance);
    NSLog(@"people----              %p" , people);
    NSLog(@"man----                 %p" , man);

La primera dirección de los tres objetos de instancia, el resultado de salida:

Imprime el contenido de la memoria de 3 objetos de instancia

De acuerdo con objc_objectla estructura de la estructura, el contenido de la memoria correspondiente debe ser Classcontenido, que es uno isa, pero aquí isase inicializa isa(es decir, contiene la Clsinformación señalada y alguna otra información). Podemos & 0x0000000ffffffff8ULLsacar la información isade apuntamientoClass correspondiente de la siguiente manera

 El correspondiente $0 $1 $2es el contenido señalado por isa, pohaga clic en

 Continúe para ver la distribución de memoria de los objetos de clase, los comandos y resultados correspondientes son los siguientes

lo encontraremos

(lldb) x/2gx 0x00007fff800d0660 //NSObject元类地址
0x7fff800d0660: 0x00007fff800d0638 0x0000000000000000 //根元类地址 + 父类地址(=NSObject类对象的地址)
(lldb) x/2gx 0x0000000100728798 //People元类地址
0x100728798: 0x0000000100728770 0x00007fff800d0660 //根元类地址 + 父类地址(=People父类的元类地址)
(lldb) x/2gx 0x0000000100728748 //Man元类地址
0x100728748: 0x0000000100728720 0x0000000100728798 //根元类地址 + 父类地址(=Man父类的元类地址)

Todas las metaclases isaapuntan al objeto de clase NSObject 对应的元类que llamamos puntero 根元类
de la metaclase raíz , y los punteros de otras metaclases secundarias apuntan a la metaclase de la clase principal correspondiente.superclassNSObjectsuperclass

2. Objeto de instancia, objeto de clase, código de prueba de objeto de metaclase:

//自定义
struct objc_classA{
  Class isa;
};

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //isa指针,实例对象,类,元类,根类,根元类
    [self testISAForObjc];
}

//isa指针,实例对象,类,元类,根类,根元类
- (void)testISAForObjc {
    Person *p =  [[Person alloc]init];//Person的实例对象
          
    Class pClass = object_getClass(p);//Person的类对象
      
    struct  objc_classA *pClassA = (__bridge struct objc_classA *)(pClass);//使用结构体转化,拿到isa
      
    Class metaPClass = object_getClass(pClass);//Person的元类对象
    
    NSLog(@"p----           %p" , p);
    NSLog(@"pClass----      %p" , pClass);
    NSLog(@"pClassA----     %p" , pClassA);
    NSLog(@"metaPClass----  %p" , metaPClass);
}

La salida es la siguiente:
 

1. La primera dirección del puntero isa del objeto de instancia es la primera dirección del objeto de instancia

2. La primera dirección del puntero isa del objeto de clase es la primera dirección del objeto de clase

3. La primera dirección del puntero isa del objeto metaclase es la primera dirección del objeto metaclase (puedes verlo de la misma manera)

4. El isa del objeto de instancia apunta al objeto de clase.

5. El isa del objeto de clase apunta al objeto de metaclase

De esto podemos ver que el principal canal de enlace de instancias, clases y metaclases son sus isapunteros.

(1) El apuntamiento del puntero isa (es decir, la conexión entre isa e instancias, clases y metaclases)

El puntero isa de un objeto de instancia objc apunta a su objeto de clase (es decir, la clase que solemos llamar), el puntero isa del objeto de clase apunta a su metaclase, el puntero isa de la metaclase apunta a la metaclase raíz, y todo metaclase isa apunta a la misma metaclase raíz A cuyo puntero isa apunta a la propia metaclase raíz. La clase padre de la superclase de la metaclase raíz apunta al objeto de la clase NSObject.

El isa de objc/instance apunta a la clase: al llamar al método del objeto, busque el objeto de la clase a través del isa del objeto de la instancia, y finalmente busque el método del objeto en el objeto de la clase, y luego ejecute el método del objeto correspondiente. de la clase apunta a la metaclase: al llamar al
método de clase, busque el objeto de metaclase a través del isa del objeto de clase, y finalmente encuentre el método de clase en el objeto de metaclase, y luego ejecute el método de clase correspondiente

Para resumir las reglas de una relación de herencia y apuntamiento entre objetos de instancia, objetos de clase y objetos de metaclase son:

1. El isa del objeto de instancia apunta a la clase y el isa de la clase apunta a la metaclase (metaClass)
2. La superclase de la clase apunta a su clase padre Si la clase es la clase raíz, el valor es nil
3. El isa de la metaclase apunta a la metaclase raíz, si Si la metaclase es la metaclase raíz, se apunta a sí misma
4. La superclase de la metaclase apunta a la metaclase padre, y si la metaclase raíz apunta a la clase raíz

(2) es una función de puntero

1. Llamar a un método de objeto de un objeto

Primero buscará el método en las listas de métodos de objc_class (clase) apuntado por su propio puntero isa.Si no puede encontrarlo, encontrará su clase principal a través del puntero super_class de objc_class (clase) y luego encontrará el método de sus listas de métodos. Si aún no se puede encontrar, continúe buscando en la estructura de la clase principal del siguiente nivel a través de super_class hasta la clase raíz

2. Llamar a un método de clase

Primero encontrará la metaclase (metaclase) a través de su propio puntero isa, y encontrará el método de clase de sus listas de métodos.Si no puede encontrarlo, encontrará la estructura de la metaclase (metaclase) de la clase principal a través del puntero super_class de la metaclase (metaclase), y luego busque el método de methodLists, si aún no lo encuentra, continúe buscando la estructura de la clase principal a través de super_class hasta la metaclase raíz (metaclase);

Atención a los detalles:

Al ejecutarse, el compilador convertirá el código en objc_msgSend(obj, @selector (makeText)).En la función objc_msgSend, primero busque la clase (clase) correspondiente a obj (objeto) a través del puntero isa de obj (objeto). En la clase (clase), primero vaya al caché para encontrar el método correspondiente (método) a través de SEL (número de método). Si no se encuentra en el caché, luego vaya a las listas de métodos para encontrarlo. Si no se encuentra en los metodistas, vaya a la superclase para encontrarlo. Si se encuentra, el método (método) se agrega al caché para facilitar la próxima búsqueda, y el puntero de función en el método (método) se usa para saltar al función correspondiente para su ejecución.

Demostración de código de muestra de GitHub

Artículo de referencia:
tiempo de ejecución (2) del desarrollo de iOS: análisis de la clase de objeto NSObject

La capa inferior OC implementa un puntero isa

La ubicación de isa y lo que hace

Supongo que te gusta

Origin blog.csdn.net/MinggeQingchun/article/details/117886677
Recomendado
Clasificación