是 - [NSObject autoContentAccessingProxy]可以工作吗?

时间:2011-11-04 21:28:12

标签: objective-c ios cocoa-touch

我正在尝试使用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:]被调用!”。

我理解如果我正在编写自己的基于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方法有误解,还是完全被打破?

2 个答案:

答案 0 :(得分:11)

您可以通过重新实现NSAutoContentAccessingProxy类的功能但没有错误来修复此错误。我写过这样一堂课:XCDAutoContentAccessingProxy。在调用autoContentAccessingProxy函数之前替换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上修复。根据{{​​3}}:

  

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

因此,只有在定位OS X 10.7或更早版本时才需要编译上述代码。

答案 1 :(得分:1)

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

这是通过在运行时添加NSAutoContentAccessingProxymethodSignatureForSelector:方法来修复forwardInvocation:类的一种硬核方法。只需将以下内容添加到项目中(不要使用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使其在设备上运行,但我不建议这样做。

对于Apple,你绝对应该file a bug report