2011-11-04 41 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或更低版​​本時,您才需要編譯上述代碼。

相關問題