2011-11-04 129 views
7

我正在尝试使用-[NSObject autoContentAccessingProxy],如http://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/nsobject_Class/Reference/Reference.html#//apple_ref/occ/instm/NSObject/autoContentAccessingProxy所述。是否 - [NSObject autoContentAccessingProxy]可以工作?

我试图代理的对象实现了NSDiscardableContent协议,而-autoContentAccessingProxy成功返回了非零值。

但是,如果我尝试向代理发送消息,我总是会得到一个NSInvalidArgumentException,原因是“*** - [NSProxy methodSignatureForSelector:] called!”。

据我所知,如果我正在编写自己的NSProxy的类,我将不得不实施-methodSignatureForSelector:方法,但在这种情况下,我不写代理,只是尝试使用由记录的方法提供的代理。对于它的价值,我可以看到代理实际上是NSAutoContentAccessingProxy类型,所以我期望该类实际上可以实现-methodSignatureForSelector:

这是一小段使用NSPurgeableData实例代替我的自定义类的代码块。这个小块有完全相同的问题。

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]; 

我是否有-autoContentAccessingProxy方法的一些误解这里,或只是彻底打破?

回答

1

你是对的,-autoContentAccessingProxy是完全坏了。 NSAutoContentAccessingProxyNSProxy的一个子类,因此如果在iOS 4或更高版本上运行,应实现methodSignatureForSelector:forwardInvocation:方法或forwardingTargetForSelector:方法。

这是通过在运行时添加methodSignatureForSelector:forwardInvocation:方法来修复NSAutoContentAccessingProxy类的硬核方式。只需将以下内容添加到您的项目中(不要使用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]; 
} 

此替代方法应仅用于演示NSAutoContentAccessingProxy如何损坏。无论如何,这只会在模拟器上工作,因为nlist调用将在设备上失败。您实际上可以使用APELite-arm中的APEFindSymbol代替nlist使其在设备上工作,但我不推荐使用它。

你应该肯定file a bug report关于苹果。

+1

哇,很好的方式来表明一个是坏的。奖励! –

11

您可以通过重新实现NSAutoContentAccessingProxy类的功能来修复此错误,但无此错误。我写过这样一个班级:XCDAutoContentAccessingProxy。在调用main函数之前,autoContentAccessingProxy方法被替换;这发生在+load方法中。所以你所要做的就是在你的应用程序中编译下面的代码,并且autoContentAccessingProxy将按预期运行。

请注意,与我以前的答案不同,您实际上可以在运输应用程序中使用此解决方案。

#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 

UPDATE此错误被固定在OS X 10.8。根据OS X Mountain Lion Release Notes

在Mac OS 10之前。8, - [NSObject autoContentAccessingProxy]返回了一个没有正确实现消息转发的对象。此代理现在在Mac OS 10.8上正常运行。

因此,只有在您针对的是OS X 10.7或更低版​​本时,您才需要编译上述代码。

相关问题