我正在尝试使用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
方法有误解,还是完全被打破?
答案 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
完全被打破了。 NSAutoContentAccessingProxy
是NSProxy
的子类,因此如果在iOS 4或更高版本上运行,则应实施methodSignatureForSelector:
和forwardInvocation:
方法或forwardingTargetForSelector:
方法。
这是通过在运行时添加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
调用将在设备上失败。实际上,您可以使用APELite-arm而不是APEFindSymbol
中的nlist
使其在设备上运行,但我不建议这样做。
对于Apple,你绝对应该file a bug report。