2012-06-22 3 views
0

Matt Gallagher가 NSInvocation을 사용하여 실행 취소 작업을하는 코드를 발견했습니다. 현재 관리부는 모든 코드에 대해 ARC를 사용하기를 원하지만 현재 모든 코드를 ARC로 변환하고 있습니다. 이 마지막 작품은 남겨진 모든 것입니다. 매트, 전환을 할 시간이 없으니 나는 여기서 도움을 얻을 수 있기를 바랬다.지원 NSInvocation 코드를 ARC 호환으로 변환 (이미 Objective-C)

도움을 주시면 감사하겠습니다.

감사합니다.

있는 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가)

 // 
     // 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

을하는 .m 당신은 모두 이러한 문제를 방지 할 수 있습니다

는 대상에서이 파일을 선택합니다> 빌드 단계> Xcode에서 소스 컴파일.

Enter 키를 누릅니다.

-fno-objc-arc 유형

완료를 누릅니다.

0

Matt가 언급했듯이 파일을 컴파일 할 때 "-fno-objc-arc"플래그를 추가해야하지만 충분하지 않습니다. 호출이 예상대로 호출되는지 확인하기 위해 두 가지 작업을 수행해야합니다.

먼저 항상 변수 선언에 "__autoreleasing"을 추가하십시오.

이 작업을 수행하지 않으면 ARC는 사용하기 전에 "호출"을 nil로 설정합니다.

그런 다음 NSInvocation 재정의에서 forwardInvocation :을 수정해야합니다. 이는 ARC가 프록시 객체에 "retain"및 "release"를 보내고 간섭을 원하지 않으므로 ("retain"및 "release"에 대한 호출을 작성하는 코드를 사용할 수 없음을 의미합니다) 메시지,하지만 아마도 당신이 아니었다).

- (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; 
    } 
    if (forwardedInvocation.selector == @selector(retain)) 
    { 
     // This branch will be followed when "retain" is invoked on the newly 
     // allocated object. ARC will call retain before the selector we want 
     // the signature of is invoked, so we need to return "self" to track 
     // the next forward invocation. 
     [forwardedInvocation setReturnValue:&self]; 
     return; 
    } 
    if (forwardedInvocation.selector == @selector(release)) 
    { 
     // This branch will be followed when "release" is invoked on the newly 
     // allocated object. ARC will call release after the selector we want 
     // the signature of is invoked. 
     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]; 
      //[forwardedInvocation retainArguments]; // added 
     } 
     // [forwardedInvocation setReturnValue:forwardedInvocation]; // added 
     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]; 
    } 
}