0

NSInvocation で Undo を機能させるために使用する、Matt Gallagher からの非常に優れたコードを見つけました。現在、経営陣はすべてのコードに ARC を使用することを望んでおり、すべてのコードを ARC に変換しています。この最後のピースが残っています。Matt さんには変換を行う時間がないので、ここで何か助けが得られることを願っていました。

任意の支援をいただければ幸いです!

ありがとうございました!

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
4

2 に答える 2

2

これを完全に回避できます:

[ターゲット] > [ビルド フェーズ] > [Xcode でソースをコンパイル] でこのファイルを選択します。

ENTER を押します。

-fno-objc-arc と入力します。

完了を押します。

于 2012-06-22T23:02:45.063 に答える