2013-02-12 8 views
11

Tôi đang sử dụng mẫu NSInvocation sau Matt Gallagher cho mã Hoàn tác/Làm lại của tôi. Mặc dù với phiên bản mới nhất của xCode tôi không nhận được một cảnh báo có nội dung: NSInvocation (ForwardedConstruction) .m: 28: 12: Class 'InvocationProxy' được xác định mà không chỉ định lớp cơ sởXác định Lớp Objective-C mà không có Lớp cơ sở - Cảnh báo Biên dịch

Mã hoạt động tuyệt vời, nhưng những người khác trong nhóm của tôi (tôi cũng vậy) không thích nhìn thấy Cảnh báo. Tôi muốn lớp được cắt càng tốt, vì vậy tôi không muốn tất cả các phương pháp trong NSObject.

Bất kỳ đề xuất nào đều được chào đón!

Cảm ơn bạn!

NSInvocation (ForwardedConstruction) .h

// 
    // NSInvocation(ForwardedConstruction).h 
    // 
    // Created by Matt Gallagher on 19/03/07. 
    // Copyright 2007 Matt Gallagher. All rights reserved. 
    // 
    // Permission is given to use this source code file without charge in any 
    // project, commercial or otherwise, entirely at your risk, with the condition 
    // that any redistribution (in part or whole) of source code must retain 
    // this copyright and permission notice. Attribution in compiled projects is 
    // appreciated but not required. 
    // 


    @interface NSInvocation (ForwardedConstruction) 

    + (id)invocationWithTarget:(id)target 
     invocationOut:(NSInvocation **)invocationOut; 
    + (id)retainedInvocationWithTarget:(id)target 
     invocationOut:(NSInvocation **)invocationOut; 

    @end 

NSInvocation (ForwardedConstuction) .m

// 
    // NSInvocation(ForwardedConstuction).m 
    // 
    // Created by Matt Gallagher on 19/03/07. 
    // Copyright 2007 Matt Gallagher. All rights reserved. 
    // 
    // Permission is given to use this source code file without charge in any 
    // project, commercial or otherwise, entirely at your risk, with the condition 
    // that any redistribution (in part or whole) of source code must retain 
    // this copyright and permission notice. Attribution in compiled projects is 
    // appreciated but not required. 
    // 

#import "NSInvocation(ForwardedConstruction).h" 
#import <objc/runtime.h> 
#import <objc/message.h> 


    // 
    // InvocationProxy is a private class for receiving invocations via the 
    // forwarding mechanism and saving the received invocation to an external 
    // invocation pointer. 
    // 
    // To avoid as many instance methods as possible, InvocationProxy is a base 
    // class (not a subclass of NSObject) and does not implement the NSObject 
    // protocol (and so is *not* a first-class object). 
    // 
@interface InvocationProxy 
{ 
    Class isa; 
    NSInvocation **invocation; 
    id target; 
    BOOL retainArguments; 
    NSUInteger forwardingAddress; 
} 

/*+ (id)alloc;*/ 
+ (void)setValuesForInstance:(InvocationProxy *)instance 
         target:(id)target 
     destinationInvocation:(NSInvocation **)destinationInvocation 
      retainArguments:(BOOL)retain; 
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector; 
- (void)forwardInvocation:(NSInvocation *)forwardedInvocation; 

@end 

#ifndef __OBJC_GC__ 

    // 
    // DeallocatorHelper is a basic object which takes an id 
    // and deallocates it when the DeallocatorHelper is deallocated. 
    // 
    // Not used in a garbage collected environment (which should deallocate the 
    // id automatically). 
    // 
@interface DeallocatorHelper : NSObject 
{ 
    id object; 
} 

- (id)initWithObject:(id)newObject; 
- (void)dealloc; 

@end 

@implementation DeallocatorHelper 

    // 
    // initWithObject 
    // 
    // Init method for objects which sets the id to be autoreleased. 
    // 
- (id)initWithObject:(id)newObject 
{ 
    self = [super init]; 
    if (self != nil) 
    { 
     object = newObject; 
    } 
    return self; 
} 

    // 
    // dealloc 
    // 
    // Deallocates the id. 
    // 
- (void)dealloc 
{ 
    NSDeallocateObject(object); 
    [super dealloc]; 
} 

@end 

#endif 

@implementation InvocationProxy 

    // 
    // initialize 
    // 
    // This empty method is required because the runtime tries to invoke it when 
    // the first message is sent to the Class. If it doesn't exist, the runtime 
    // gets mad. 
    // 
+ (void)initialize 
{ 
} 


    // 
    // alloc 
    // 
    // Allocator for the class. Also sets the 
    // 
+ (id)alloc 
{ 
     // 
     // Allocate the object using the default allocator. 
     // 
    InvocationProxy *newObject = 
#ifdef __OBJC_GC__ 
    objc_allocate_object(self, 0); 
#else 
    NSAllocateObject(self, 0, nil); 
#endif 
    return newObject; 
} 


- (id)init { 
    return self; 
} 

    // 
    // setValuesForInstance:target:destinationInvocation:retainArguments: 
    // 
    // Method to set the attributes on the instance passed in. We use a class 
    // method instead of an instance method to avoid extra instance methods on 
    // the class. 
    // 
+ (void)setValuesForInstance:(InvocationProxy *)instance 
         target:(id)destinationTarget 
     destinationInvocation:(NSInvocation **)destinationInvocation 
      retainArguments:(BOOL)retain; 
{ 
    instance->target = destinationTarget; 
    instance->invocation = destinationInvocation; 
    instance->retainArguments = retain; 
} 

    // 
    // methodSignatureForSelector: 
    // 
    // Invoked by the runtime whenever a message is sent for a method that doesn't 
    // exist. 
    // 
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector 
{ 
     // 
     // This method should be invoked once before attributes are set (as an 
     // "init" invocation). 
     // 
    if (target == nil) 
    { 
      // 
      // If the invocation is something other than "init", complain using 
      // NSObject's standard doesNotRecognizeSelector: 
      // 
     if (aSelector != @selector(init)) 
     { 
      SEL failSEL = @selector(doesNotRecognizeSelector:); 
      Method failMethod = 
      class_getInstanceMethod([NSObject class], failSEL); 
      IMP failImp = method_getImplementation(failMethod); 
      failImp(self, failSEL, aSelector); 
     } 

      // 
      // Otherwise, we use the forwarded "init" to preserve the return 
      // address of the forwarding code (which we can use later to determine 
      // if this is a forwarded or direct invocation). 
      // 
     forwardingAddress = (NSUInteger)__builtin_return_address(0); 

      // 
      // Return the NSMethodSignature from NSObject's init method (just 
      // so we have something to return). 
      // 
     return [NSObject instanceMethodSignatureForSelector:aSelector]; 
    } 

     // 
     // On subsequent invocations, check if we are a forwarded invocation or 
     // a direct invocation. 
     // 
    NSUInteger returnAddress = (NSUInteger)__builtin_return_address(0); 
    if (returnAddress != forwardingAddress) 
    { 
      // 
      // Handle the case where methodSignatureForSelector: is the message sent 
      // directly to the proxy. 
      // 
      // There is a chance that we have guessed wrong (i.e. if this is sent 
      // from __forward__ but from a different code branch) but that won't 
      // cause a fatal problem, just a redundant autoreleased NSInvocation 
      // that will get safely autoreleased and ignored. 
      // 
      // Create an NSInvocation for methodSignatureForSelector: 
      // 
     NSMethodSignature *signature = 
     [target methodSignatureForSelector:_cmd]; 
     *invocation = 
     [NSInvocation invocationWithMethodSignature:signature]; 
     [*invocation setTarget:target]; 
     [*invocation setSelector:_cmd]; 
     [*invocation setArgument:&aSelector atIndex:2]; 
     if (retainArguments) 
     { 
      [*invocation retainArguments]; 
     } 

      // 
      // Deliberately fall through and still return the target's 
      // methodSignatureForSelector: result (in case we guessed wrong). 
      // 
    } 

     // 
     // This is the "normal" case: after initialization, we have been correctly 
     // invoked from the forwarding code. Return the target's 
     // methodSignatureForSelector: for the given selector. 
     // 
    NSMethodSignature *signature = 
    [target methodSignatureForSelector:aSelector]; 

    NSAssert3(signature != nil, 
       @"NSInvocation(ForwardedConstruction) error: object 0x%@ of class '%@' does not implement %s", 
       target, [target className], sel_getName(aSelector)); 

    return signature; 
} 

    // 
    // forwardInvocation: 
    // 
    // This method is invoked by message forwarding. 
    // 
- (void)forwardInvocation:(NSInvocation *)forwardedInvocation 
{ 
     // 
     // This method will be invoked once on initialization (before target is set). 
     // Do nothing. 
     // 
    if (target == nil) 
    { 
      // 
      // This branch will be followed when "init" is invoked on the newly 
      // allocated object. Since "init" returns "self" we need to set that 
      // on the forwardedInvocation. 
      // 
     [forwardedInvocation setReturnValue:&self]; 
     return; 
    } 

     // 
     // Check if the target of the forwardedInvocation is equal to self. If 
     // it is, then this is a genuine forwardedInvocation. If it isn't, then 
     // forwardInvocation: was directly the message sent to this proxy. 
     // 
    if ([forwardedInvocation target] == self) 
    { 
     [forwardedInvocation setTarget:target]; 
     *invocation = forwardedInvocation; 
     if (retainArguments) 
     { 
      [*invocation retainArguments]; 
     } 
     return; 
    } 

     // 
     // Handle the case where forwardedInvocation is the message sent directly 
     // to the proxy. We create an NSInvocation representing a forwardInvocation: 
     // sent to the target instead. 
     // 
    NSMethodSignature *signature = 
    [target methodSignatureForSelector:_cmd]; 
    *invocation = 
    [NSInvocation invocationWithMethodSignature:signature]; 
    [*invocation setTarget:target]; 
    [*invocation setSelector:_cmd]; 
    [*invocation setArgument:&forwardedInvocation atIndex:2]; 
    if (retainArguments) 
    { 
     [*invocation retainArguments]; 
    } 
} 

@end 

@implementation NSInvocation (ForwardedConstruction) 

    // 
    // invocationWithTarget:invocationOut: 
    // 
    // Basic constructor for NSIncoation using forwarded construction. 
    // 
+ (id)invocationWithTarget:(id)target 
      invocationOut:(NSInvocation **)invocationOut 
{ 
     // 
     // Check that invocationOut isn't nil. 
     // 
    NSAssert2(target != nil && invocationOut != nil, 
       @"%@ method %s requires target that isn't nil and a valid NSInvocation** for the second parameter", 
       [self className], sel_getName(_cmd)); 

     // 
     // Alloc and init the proxy 
     // 
    InvocationProxy *invocationProxy = [[InvocationProxy alloc] init]; 

     // 
     // Set the instance attributes on the proxy 
     // 
    [InvocationProxy 
    setValuesForInstance:invocationProxy 
    target:target 
    destinationInvocation:invocationOut 
    retainArguments:NO]; 

     // 
     // Create the DeallocatorHelper if needed 
     // 
#ifndef __OBJC_GC__ 
     [[[DeallocatorHelper alloc] 
       initWithObject:invocationProxy] 
      autorelease]; 
#endif 

    return invocationProxy; 
} 

    // 
    // retainedInvocationWithTarget:invocationOut: 
    // 
    // Same as above but sends retainArguments to the NSInvocation created. 
    // 
+ (id)retainedInvocationWithTarget:(id)target 
        invocationOut:(NSInvocation **)invocationOut 
{ 
     // 
     // Check that invocationOut isn't nil. 
     // 
    NSAssert2(target != nil && invocationOut != nil, 
       @"%@ method %s requires target that isn't nil and a valid NSInvocation** for the second parameter", 
       [self className], sel_getName(_cmd)); 

     // 
     // Alloc and init the proxy 
     // 
    InvocationProxy *invocationProxy = [[InvocationProxy alloc] init]; 

     // 
     // Set the instance attributes on the proxy 
     // 
    [InvocationProxy 
    setValuesForInstance:invocationProxy 
    target:target 
    destinationInvocation:invocationOut 
    retainArguments:YES]; 

     // 
     // Create the DeallocatorHelper if needed 
     // 
#ifndef __OBJC_GC__ 
    [[[DeallocatorHelper alloc] 
     initWithObject:invocationProxy] 
    autorelease]; 
#endif 

    return invocationProxy; 
} 

@end 
+2

Có gì sai khi có phương pháp của NSObject? Họ không thực sự chi phí bất cứ điều gì, khác hơn một vài mục trong phương pháp hashtable. –

+0

Có lẽ mở rộng 'NSProxy' thay vì' NSObject' hoạt động cho trường hợp này. – rmaddy

Trả lời

25

Sử dụng NS_ROOT_CLASS hoặc pragma -Wobjc-root-class để tắt cảnh báo lớp gốc. Ví dụ:

NS_ROOT_CLASS 
@interface InvocationProxy 
@end 

@implementation InvocationProxy 
@end 

Với pragma nó sẽ là:

#pragma clang diagnostic push 
#pragma clang diagnostic ignored "-Wobjc-root-class" 
@interface InvocationProxy 
#pragma clang diagnostic pop 
@end 

@implementation InvocationProxy 
@end 
+0

Hoàn hảo! Cảm ơn bạn! – scooter133

3

Dường như sự chểnh mảng của thừa kế từ NSObject là do thiết kế cho thư viện (theo ý kiến), vì vậy tôi đề nghị bạn remove the warning using a pragma.

#pragma GCC diagnostic ignored "-W<warningcode>" 
0

Giải pháp nêu trên là đúng. Cá nhân tôi sử dụng MACRO đơn giản này:

#import <Foundation/Foundation.h> 

OBJC_ROOT_CLASS 

@interface RootClassName 


@end