【问题标题】:Does -[NSObject autoContentAccessingProxy] work at all?-[NSObject autoContentAccessingProxy] 是否有效?
【发布时间】:2011-11-04 21:28:12
【问题描述】:

我正在尝试使用-[NSObject autoContentAccessingProxy],如http://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/nsobject_Class/Reference/Reference.html#//apple_ref/occ/instm/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方法有什么误解,还是完全坏掉了?

【问题讨论】:

    标签: objective-c ios cocoa-touch


    【解决方案1】:

    您可以通过重新实现 NSAutoContentAccessingProxy 类的功能来修复此错误,但没有错误。我写过这样一个类:XCDAutoContentAccessingProxyautoContentAccessingProxy 方法在调用 main 函数之前被替换;这发生在+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
    

    更新 此错误已在 OS X 10.8 上修复。根据OS X Mountain Lion Release Notes

    在 Mac OS 10.8 之前,-[NSObject autoContentAccessingProxy] 返回的对象未正确实现消息转发。此代理现在可以在 Mac OS 10.8 上正常运行。

    因此,仅当您面向 OS X 10.7 或更早版本时才需要编译上述代码。

    【讨论】:

      【解决方案2】:

      你说得对,-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 将此事告知 Apple。

      【讨论】:

        猜你喜欢
        • 2017-02-17
        • 2011-03-04
        • 1970-01-01
        • 1970-01-01
        • 2014-09-16
        • 2017-06-20
        • 2019-10-23
        • 1970-01-01
        • 2013-08-18
        相关资源
        最近更新 更多