2011-11-04 12 views
7

Estoy tratando de hacer uso de -[NSObject autoContentAccessingProxy] como se describe en http://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/nsobject_Class/Reference/Reference.html#//apple_ref/occ/instm/NSObject/autoContentAccessingProxy.¿Funciona [NSObject autoContentAccessingProxy] en absoluto?

El objeto que intento representar implementa el protocolo NSDiscardableContent y -autoContentAccessingProxy devuelve un valor que no es nulo.

Si, sin embargo, intento enviar un mensaje al proxy, siempre obtengo un NSInvalidArgumentException con un motivo de "*** - [NSProxy methodSignatureForSelector:] llamado!".

Entiendo que si estuviera escribiendo mi propia clase basada en NSProxy, tendría que implementar el método -methodSignatureForSelector:, pero en este caso, no estoy escribiendo el proxy, solo trato de usar el proxy proporcionado por el método documentado . Por lo que vale, puedo ver que el proxy es realmente del tipo NSAutoContentAccessingProxy, por lo que cabría esperar que esa clase tuviera una implementación para -methodSignatureForSelector:.

Aquí hay un pequeño bloque de código que utiliza una instancia de NSPurgeableData en lugar de mi clase personalizada. Este pequeño bloque tiene exactamente el mismo problema.

NSPurgeableData * data = [NSPurgeableData dataWithBytes:"123" length:3]; 
NSLog(@"data.length = %u", data.length); 
id proxyData = [data autoContentAccessingProxy]; 
NSLog(@"proxyData.length = %u", [proxyData length]); // throws NSInvalidArgumentException! 
[data endContentAccess]; 
[data release]; 

¿Tengo algún malentendido del método -autoContentAccessingProxy aquí, o es sólo roto por completo?

Respuesta

1

Tiene toda la razón, -autoContentAccessingProxy está totalmente roto. NSAutoContentAccessingProxy es una subclase de NSProxy y, por lo tanto, debe implementar los métodos methodSignatureForSelector: y forwardInvocation: o el método forwardingTargetForSelector: si se ejecuta en iOS 4 o posterior.

Aquí es una forma grave de la fijación de la clase NSAutoContentAccessingProxy mediante la adición de los métodos methodSignatureForSelector: y forwardInvocation: en tiempo de ejecución. Simplemente agregue lo siguiente a su proyecto (no compile esto con ARC).

#import <mach-o/dyld.h> 
#import <mach-o/nlist.h> 

__attribute__((constructor)) void FixAutoContentAccessingProxy(void); 
static id _target(id autoContentAccessingProxy); 
static NSMethodSignature *NSAutoContentAccessingProxy_methodSignatureForSelector(id self, SEL _cmd, SEL selector); 
static void NSAutoContentAccessingProxy_forwardInvocation(id self, SEL _cmd, NSInvocation *invocation); 

__attribute__((constructor)) void FixAutoContentAccessingProxy(void) 
{ 
    Class NSAutoContentAccessingProxy = objc_lookUpClass("NSAutoContentAccessingProxy"); 
    Method methodSignatureForSelector = class_getInstanceMethod([NSObject class], @selector(methodSignatureForSelector:)); 
    Method forwardInvocation = class_getInstanceMethod([NSObject class], @selector(forwardInvocation:)); 
    class_addMethod(NSAutoContentAccessingProxy, @selector(methodSignatureForSelector:), (IMP)NSAutoContentAccessingProxy_methodSignatureForSelector, method_getTypeEncoding(methodSignatureForSelector)); 
    class_addMethod(NSAutoContentAccessingProxy, @selector(forwardInvocation:), (IMP)NSAutoContentAccessingProxy_forwardInvocation, method_getTypeEncoding(forwardInvocation)); 
} 

static id _target(id autoContentAccessingProxy) 
{ 
    static uint32_t targetIvarOffset; 
    static dispatch_once_t once; 
    dispatch_once(&once, ^{ 
     struct nlist symlist[] = {{"_OBJC_IVAR_$_NSAutoContentAccessingProxy._target", 0, 0, 0, 0}, NULL}; 
     for(uint32_t i = 0; i < _dyld_image_count(); i++) 
     { 
      if (nlist(_dyld_get_image_name(i), symlist) == 0 && symlist[0].n_value != 0) 
      { 
       uint32_t *_OBJC_IVAR_NSAutoContentAccessingProxy_target = (uint32_t*)((uint32_t)_dyld_get_image_header(i) + symlist[0].n_value); 
       targetIvarOffset = *_OBJC_IVAR_NSAutoContentAccessingProxy_target; 
       break; 
      } 
     } 
    }); 

    return *(id*)((uint32_t)autoContentAccessingProxy + targetIvarOffset); 
} 

static NSMethodSignature *NSAutoContentAccessingProxy_methodSignatureForSelector(id self, SEL _cmd, SEL selector) 
{ 
    return [_target(self) methodSignatureForSelector:selector]; 
} 

static void NSAutoContentAccessingProxy_forwardInvocation(id self, SEL _cmd, NSInvocation *invocation) 
{ 
    [invocation setTarget:_target(self)]; 
    [invocation invoke]; 
} 

Esta solución alternativa se debe utilizar solo para demostrar cómo se ha roto NSAutoContentAccessingProxy. De todos modos, esto solo funcionará en el simulador porque la llamada nlist fallará en el dispositivo. De hecho, podría hacer que funcione en el dispositivo utilizando APEFindSymbol desde APELite-arm en lugar de nlist, pero no lo recomiendo.

Definitivamente debe file a bug report al respecto a Apple.

+1

Guau, buena manera de demostrar que está roto. ¡Prestigio! –

11

Puede solucionar este error volviendo a implementar lo que hace la clase NSAutoContentAccessingProxy pero sin los errores. Escribí tal clase: XCDAutoContentAccessingProxy. El método autoContentAccessingProxy se reemplaza antes de llamar a su función main; esto sucede en el método +load. Entonces, todo lo que tiene que hacer es compilar el siguiente código en su aplicación y autoContentAccessingProxy se comportará como se espera.

Tenga en cuenta que a diferencia de mi respuesta anterior, puede utilizar esta solución en una aplicación de envío.

#if !__has_feature(objc_arc) 
#error This code must be compiled with Automatic Reference Counting (CLANG_ENABLE_OBJC_ARC/-fobjc-arc) 
#endif 


#import <Foundation/Foundation.h> 
#import <objc/runtime.h> 


@interface XCDAutoContentAccessingProxy : NSProxy 

+ (XCDAutoContentAccessingProxy *) proxyWithTarget:(id)target; 

@property (nonatomic, strong) id target; 

@end 


@implementation XCDAutoContentAccessingProxy 

@synthesize target = _target; 

static id autoContentAccessingProxy(id self, SEL _cmd) 
{ 
    return [XCDAutoContentAccessingProxy proxyWithTarget:self]; 
} 

+ (void) load 
{ 
    method_setImplementation(class_getInstanceMethod([NSObject class], @selector(autoContentAccessingProxy)), (IMP)autoContentAccessingProxy); 
} 

+ (XCDAutoContentAccessingProxy *) proxyWithTarget:(id)target 
{ 
    if (![target conformsToProtocol:@protocol(NSDiscardableContent)]) 
     return nil; 

    if (![target beginContentAccess]) 
     return nil; 

    XCDAutoContentAccessingProxy *proxy = [self alloc]; 
    proxy.target = target; 
    return proxy; 
} 

- (void) dealloc 
{ 
    [self.target endContentAccess]; 
} 

- (void) finalize 
{ 
    [self.target endContentAccess]; 

    [super finalize]; 
} 

- (id) forwardingTargetForSelector:(SEL)selector 
{ 
    return self.target; 
} 

- (NSMethodSignature *) methodSignatureForSelector:(SEL)selector 
{ 
    return [self.target methodSignatureForSelector:selector]; 
} 

- (void) forwardInvocation:(NSInvocation *)invocation 
{ 
    [invocation setTarget:self.target]; 
    [invocation invoke]; 
} 

@end 

ACTUALIZACIÓN Este error se corrige en OS X 10.8. De acuerdo con OS X Mountain Lion Release Notes:

Antes de Mac OS 10.8, - [NSObject autoContentAccessingProxy] devolvió un objeto que no implementó correctamente el reenvío de mensajes. Este proxy ahora se comporta correctamente en Mac OS 10.8.

Por lo tanto, debe compilar el código anterior solo si se dirige a OS X 10.7 o una versión anterior.

Cuestiones relacionadas