2011-11-04 42 views
7

Tôi đang cố gắng sử dụng -[NSObject autoContentAccessingProxy] như được mô tả tại http://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/nsobject_Class/Reference/Reference.html#//apple_ref/occ/instm/NSObject/autoContentAccessingProxy.Does - [NSObject autoContentAccessingProxy] có hoạt động không?

Đối tượng tôi đang cố gắng proxy triển khai giao thức NSDiscardableContent-autoContentAccessingProxy trả về thành công giá trị không phải là số không.

Nếu, tuy nhiên, tôi cố gắng gửi thư tới proxy, tôi luôn nhận được NSInvalidArgumentException với lý do "*** - [Phương thức NSProxySignatureForSelector:] được gọi!".

Tôi hiểu rằng nếu tôi đang viết riêng NSProxy lớp dựa trên của tôi, tôi sẽ phải thực hiện các phương pháp -methodSignatureForSelector:, nhưng trong trường hợp này, tôi không viết proxy, chỉ cần cố gắng sử dụng proxy được cung cấp bởi các phương pháp ghi nhận . Đối với những gì nó có giá trị, tôi có thể thấy rằng các proxy thực sự là loại NSAutoContentAccessingProxy, vì vậy tôi mong đợi rằng lớp đó thực sự sẽ có một thực hiện cho -methodSignatureForSelector:.

Đây là một khối mã nhỏ sử dụng một cá thể NSPurgeableData thay vì lớp tùy chỉnh của tôi. Khối nhỏ này có cùng một vấn đề.

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

Tôi có hiểu nhầm về phương pháp -autoContentAccessingProxy ở đây hay chỉ hoàn toàn bị hỏng?

Trả lời

1

Bạn hoàn toàn đúng, -autoContentAccessingProxy hoàn toàn bị hỏng. NSAutoContentAccessingProxy là một phân lớp của NSProxy và do đó nên triển khai các phương pháp methodSignatureForSelector:forwardInvocation: hoặc phương pháp forwardingTargetForSelector: nếu chạy trên iOS 4 trở lên.

Đây là cách khắc phục sự cố của lớp học NSAutoContentAccessingProxy bằng cách thêm các phương thức methodSignatureForSelector:forwardInvocation: khi chạy. Chỉ cần thêm phần sau vào dự án của bạn (không biên dịch với 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]; 
} 

Cách giải quyết này chỉ nên được sử dụng để minh họa cách NSAutoContentAccessingProxy bị hỏng. Dù sao, điều này sẽ chỉ hoạt động trên trình mô phỏng vì cuộc gọi nlist sẽ không thành công trên thiết bị. Bạn thực sự có thể làm cho nó hoạt động trên thiết bị bằng cách sử dụng APEFindSymbol từ APELite-arm thay vì nlist nhưng tôi không khuyên bạn nên sử dụng nó.

Bạn chắc chắn nên file a bug report về việc này với Apple.

+1

Thật tuyệt vời, cách tốt nhất để thể hiện rằng trang web đã bị hỏng. Thanh danh! –

11

Bạn có thể khắc phục lỗi này bằng cách thực hiện lại những gì lớp NSAutoContentAccessingProxy thực hiện nhưng không có lỗi. Tôi đã viết một lớp học như vậy: XCDAutoContentAccessingProxy. Phương pháp autoContentAccessingProxy được thay thế trước khi bạn gọi hàm main; điều này xảy ra theo phương pháp +load. Vì vậy, tất cả những gì bạn phải làm là biên dịch mã sau trong ứng dụng của bạn và autoContentAccessingProxy sẽ hoạt động như mong đợi.

Lưu ý rằng không giống như câu trả lời trước đây của tôi, bạn thực sự có thể sử dụng giải pháp này trong ứng dụng giao hàng.

#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 

CẬP NHẬT Lỗi này được khắc phục trên OS X 10.8. Theo số OS X Mountain Lion Release Notes:

Trước Mac OS 10.8, - [NSObject autoContentAccessingProxy] trả về một đối tượng không thực hiện đúng cách chuyển tiếp tin nhắn. Proxy này hiện hành xử đúng trên Mac OS 10.8.

Vì vậy, bạn chỉ cần biên dịch mã trên nếu bạn đang nhắm mục tiêu OS X 10.7 trở xuống.

Các vấn đề liên quan