Работает ли [NSObject autoContentAccessingProxy] вообще? - PullRequest
7 голосов
/ 05 ноября 2011

Я пытаюсь использовать -[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:] звонил!».

Я понимаю, что если бы я писал свой собственный класс 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 ]

11 голосов
/ 07 февраля 2012

Вы можете исправить эту ошибку, переопределив то, что делает класс 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. Согласно Замечания к выпуску OS X Mountain Lion :

До Mac OS 10.8 - [NSObject autoContentAccessingProxy] возвращал объект, который неправильно реализовал пересылку сообщений. Этот прокси теперь работает правильно в Mac OS 10.8.

Таким образом, вам нужно скомпилировать приведенный выше код, только если вы нацелены на OS X 10.7 или более раннюю версию.

1 голос
/ 06 февраля 2012

Вы абсолютно правы, -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];
}

Этот обходной путь следует использовать только для демонстрации нарушения NSAutoContentAccessingProxy. В любом случае, это будет работать только на симуляторе, потому что на устройстве произойдет сбой вызова nlist. Вы могли бы фактически заставить его работать на устройстве, используя APEFindSymbol из APELite-arm вместо nlist, но я не рекомендую это.

Вы обязательно должны подать отчет об ошибке об этом в Apple.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...