Does - [NSObject autoContentAccessingProxy] work at all?

I am trying to use -[NSObject autoContentAccessingProxy] as described in http://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/nsobject_Class/Reference/Reference.html#//apple_ref/occ/ instm / NSObject / autoContentAccessingProxy .

The object I'm trying to execute the proxy implements the NSDiscardableContent protocol and -autoContentAccessingProxy successfully returns a non-zero value.

If, however, I try to send a proxy message, I always get an NSInvalidArgumentException with the reason "*** - [NSProxy methodSignatureForSelector:] being called!".

I understand that if I were writing my own class based on NSProxy , I would have to implement the -methodSignatureForSelector: method, but in this case I am not writing a proxy, just trying to use the proxy server provided by the documented method. For what it's worth, I see that the proxy is of type NSAutoContentAccessingProxy , so I would expect this class to really have an implementation for -methodSignatureForSelector:

Here is a small block of code using an instance of NSPurgeableData instead of my custom class. This small block has exactly the same problem.

 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]; 

Do I have some kind of misunderstanding of the -autoContentAccessingProxy method here, or is it just completely broken?

+7
source share
2 answers

You can fix this error by overriding the NSAutoContentAccessingProxy class, but without errors. I wrote a class like this: XCDAutoContentAccessingProxy . The autoContentAccessingProxy method autoContentAccessingProxy replaced before the main function is called; this happens in the +load method. So, all you have to do is compose the following code in your application, and autoContentAccessingProxy will behave as expected.

Please note that unlike my previous answer, you can use this solution in the application for delivery.

 #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 

UPDATE This bug is fixed in OS X 10.8. According to OS X Mountain Lion Release Notes :

Prior to Mac OS 10.8, [NSObject autoContentAccessingProxy] returned an object that did not properly forward messages. This proxy now behaves correctly on Mac OS 10.8.

So, you need to compile the above code only if you are targeting OS X 10.7 or earlier.

+11
source

You are absolutely right, -autoContentAccessingProxy completely broken. NSAutoContentAccessingProxy is a subclass of NSProxy and therefore must implement the methodSignatureForSelector: and forwardInvocation: methods or the forwardingTargetForSelector: method when running on iOS 4 or higher.

Here is a hardcore way to set the NSAutoContentAccessingProxy class by adding the methodSignatureForSelector: and forwardInvocation: methods at run time. Just add the following to your project (do not compile this with 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]; } 

This workaround should only be used to demonstrate how NSAutoContentAccessingProxy violated. In any case, this will only work on the simulator, because the nlist call nlist not work on the device. You could make it work on the device using APEFindSymbol from APELite-arm instead of nlist , but I do not recommend it.

You must write a bug report about this to Apple.

+1
source

All Articles