13

我正在使用Matt GallagherNSInvocation的以下代码作为我的撤消/重做代码。虽然使用最新版本的 xCode 我没有收到警告说:NSInvocation(ForwardedConstruction).m:28:12: Class 'InvocationProxy' defined without specified a base class

代码运行良好,但我团队中的其他人(我也是)不喜欢看到警告。我希望类尽可能精简,所以我不希望 NSObject 中的所有方法。

欢迎任何建议!

谢谢!

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(ForwardedConstruction).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
4

3 回答 3

28

使用NS_ROOT_CLASSor pragma-Wobjc-root-class禁用根类警告。例子:

NS_ROOT_CLASS
@interface InvocationProxy
@end

@implementation InvocationProxy
@end

使用 pragma 将是:

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

@implementation InvocationProxy
@end
于 2013-02-12T21:50:13.427 回答
3

似乎从 NSObject 省略继承是库的设计(根据评论),所以我建议你使用 pragma 删除警告

#pragma GCC diagnostic ignored "-W<warningcode>"
于 2013-02-12T21:36:39.393 回答
0

上面的解决方案是对的。我个人使用这个简单的宏:

#import <Foundation/Foundation.h>

OBJC_ROOT_CLASS

@interface RootClassName


@end
于 2014-12-29T00:20:32.417 回答