Cómo OC implementa la herencia múltiple

Cuando la herencia única no es suficiente y es difícil modelar el dominio del problema, normalmente pensamos directamente en la herencia múltiple. La herencia múltiple es la capacidad de derivar clases de más de una clase base directa, que puede modelar aplicaciones de forma más directa. Sin embargo, Objective-C no admite herencia múltiple. Debido a que la búsqueda del nombre del mecanismo del mensaje ocurre en tiempo de ejecución en lugar de en tiempo de compilación, es difícil resolver la ambigüedad que puede ser causada por múltiples clases base. Pero, de hecho, Objective-C no necesita admitir herencia múltiple. Podemos encontrar los siguientes métodos para lograr la herencia múltiple indirectamente:

** Reenvío de mensajes **
** delegado y protocolo **
** categoría **

Reenvío de mensajes

Cuando se envía un mensaje a someObject, pero el sistema en tiempo de ejecución no puede encontrar la implementación del método correspondiente en la clase actual y la clase principal, el sistema en tiempo de ejecución no informará inmediatamente un error para bloquear el programa, sino que realizará los siguientes pasos en secuencia:

 

Describa brevemente el proceso por separado:

1. Análisis de método dinámico: envíe resolveInstanceMethod: señal a la clase actual para verificar si un método se agrega dinámicamente a la clase. (Si está confundido, busque: @dynamic)
2. Reenvío rápido de mensajes: compruebe si la clase implementa el método forwardingTargetForSelector:, si está implementado, llame a este método. Si el objeto de valor de retorno de este método no es nil o self, el mensaje se reenviará al objeto de retorno.
3. Reenvío de mensajes estándar: runtime envía methodSignatureForSelector: mensaje para obtener la firma del método correspondiente al Selector. Si el valor devuelto no está vacío, el mensaje se reenviará a través de forwardInvocation :, y si el valor devuelto está vacío, el mensaje doesNotRecognizeSelector: se enviará al objeto actual y el programa se bloqueará y se cerrará.

Como su nombre lo indica, podemos usar los métodos 2 y 3 en el proceso anterior para completar el reenvío de mensajes.

Reenvío rápido de mensajes

El método de implementación del reenvío rápido de mensajes es muy simple, solo necesita reescribir- (id) forwardingTargetForSelector: (SEL) aSelector método.
Permítanme darles un ejemplo simple. Por ejemplo, hay dos clases existentes: Maestro y Médico. El médico puede realizar cirugía (método de operación).

@interface Teacher : NSObject   
  
@end   
@interface Doctor : NSObject   
   
- (void)operate;   
@end   

A través del reenvío rápido de mensajes, es fácil para el maestro llamar al método quirúrgico del médico.

La clase del profesor debe enviar el mensaje al médico:

- (id)forwardingTargetForSelector:(SEL)aSelector   
{   
   Doctor *doctor = [[Doctor alloc]init];   
   if ([doctor respondsToSelector:aSelector]) {   
       return doctor;   
   }   
   return nil;   
}   

Si bien el mensaje se puede reenviar y transmitir dinámicamente, la verificación estática del editor no se puede omitir, entonces la pregunta es, ¿cómo declararlo ya que la clase Teacher no implementa el método de operación?
Hasta ahora, solo pensé en los siguientes dos métodos:

Método de declaración 1 ———— Categoría

@interface Teacher (DoctorMethod)   
- (void)operate;   
  
@end   

Método de declaración 2 ———— Importar archivo de encabezado, forzar conversión de tipo al llamar

El archivo de encabezado de Teacher debe incluir el archivo de encabezado de Doctor, indicando al compilador que busque la declaración del método del operador en Doctor.h, y fuerce el cambio de tipo al llamar.

Teacher *teacher = [[Teacher alloc]init];   
[(Doctor *)teacher operate];   

Si está interesado, puede pensar en una pregunta: si convierte su tipo en id, también puede compilarlo y pasarlo y realizar el reenvío. Pero, ¿qué peligros ocultos traerá?

El método 1 utiliza categorías que son lo suficientemente claras y simples, entonces, ¿por qué proponer el método 2? Mi pensamiento es que el inconveniente del Método 1 es que el método descartado está arreglado y expuesto en .h; El Método 2 es relativamente flexible y oculta el mensaje que quiero reenviar.

Reenvío de mensajes estándar El reenvío de mensajes
estándar necesita reescribir los dos métodos methodSignatureForSelector: y forwardInvocation:.
El proceso de envío se muestra en la figura:

 

Método de reescritura hacia adelante:

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector   
{   
    NSMethodSignature* signature = [super methodSignatureForSelector:aSelector];   
    if (signature==nil) {   
        signature = [someObj methodSignatureForSelector:aSelector];   
    }   
    NSUInteger argCount = [signature numberOfArguments];   
    for (NSInteger i=0 ; i

    }   
       
    return signature;   
}   
 
- (void)forwardInvocation:(NSInvocation *)anInvocation   
{   
    SEL seletor = [anInvocation selector];   
    if ([someObj respondsToSelector:seletor]) {   
        [anInvocation invokeWithTarget:someObj];   
    }   
       
}   

Comparación de dos métodos de reenvío de mensajes

Reenvío rápido de mensajes: simple, rápido, pero solo se puede reenviar a un objeto.
Reenvío de mensajes estándar: un poco más complicado y más lento, pero la operación de reenvío es controlable y se puede realizar el reenvío de múltiples objetos.

delegar 和 protocolo

La delegación es el mecanismo de devolución de llamada más utilizado en Objective-C. Uso No creo que haya mucho que decir. Para resumir las características del mecanismo: el
delegado ayuda al cuerpo principal a completar la tarea de operación, y la operación que necesita personalizarse está reservada para que el objeto delegado personalice la implementación, similar al cuerpo principal de subclases.
Además, se puede utilizar como detector de eventos.
No puedo pensar en eso por un tiempo ...

Categoría
Personalmente, creo que la categoría es una de las esencias del diseño de Objective-C, y también es la razón principal por la que amo Objective-C.

La categoría es algo poderoso, puede agregar métodos a la clase, también puede agregar instancias. Debe haber muchas personas que no estén de acuerdo y quieran recordarme: una de las limitaciones de las categorías es que no pueden agregar nuevas variables de instancia a la clase. El respaldo realmente está arruinando, escúchame dar un ejemplo y hablar despacio.

Recreemos la clase del profesor:

@interface Teacher : NSObject   
{   
    NSUInteger age;   
}   
   
@end   

Solo una edad no es suficiente para describir al maestro, quiero agregar una instancia de profesión para salvar la profesión del maestro. La idea intuitiva es subclasificar Teacher, de hecho, también puede usar categorías.

Debe comprender los conocimientos de programación en tiempo de ejecución, prestar atención a objc_setAssociatedObject y objc_getAssociatedObject.

//   
//  Teacher+Profession.m   
//     
  
#import "Teacher+Profession.h"   
#import   

  
const char *ProfessionType = "NSString *";   
@implementation Teacher (Profession)   
  
-(void)setProf:(NSString*)prof   
{   
   objc_setAssociatedObject(self, ProfessionType, prof, OBJC_ASSOCIATION_RETAIN_NONATOMIC);   
}   
  
-(NSString *)prof   
{   
   NSString *pro = objc_getAssociatedObject(self, ProfessionType);   
   return pro;   
}   
  
@end   

Ahora puede acceder al valor de la profesión docente a través de setProf: y prof.



Autor: ScaryMonsterLyn
Enlace: https: //www.jianshu.com/p/c473b41c083d

Supongo que te gusta

Origin blog.csdn.net/wangletiancsdn/article/details/97629933
Recomendado
Clasificación