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

GBe*_*gen 7 cocoa-touch objective-c ios

我正在尝试使用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];
Run Code Online (Sandbox Code Playgroud)

我在-autoContentAccessingProxy这里对这种方法有一些误解,还是只是完全被打破了?

0xc*_*ced 11

您可以通过重新实现NSAutoContentAccessingProxy类的功能但没有错误来修复此错误.我写了这样一堂课:XCDAutoContentAccessingProxy.autoContentAccessingProxymain调用函数之前替换该方法; 这种情况发生在+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
Run Code Online (Sandbox Code Playgroud)

更新此错误已在OS X 10.8上修复.根据OS X Mountain Lion发行说明:

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

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


0xc*_*ced 1

你说得完全正确,-autoContentAccessingProxy完全崩溃了。NSAutoContentAccessingProxy是 的子类NSProxy,因此应该实现methodSignatureForSelector:forwardInvocation:方法,或者forwardingTargetForSelector:如果在 iOS 4 或更高版本上运行,则应该实现 和 方法。

这是通过在运行时NSAutoContentAccessingProxy添加methodSignatureForSelector:和方法来修复类的核心方法。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];
}
Run Code Online (Sandbox Code Playgroud)

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

您绝对应该向 Apple提交有关它的错误报告