2014-12-30 5 views
3

arm64 아키텍처를 지원하는 하나의 이전 프로젝트를 준비했습니다. 하지만 64 비트 장치에서이 코드를 실행하려고하면 [호출 retainArguments]에서 EXC_BAD_ACCESS 충돌이 발생합니다. lineNSInvocation을 사용할 때 arm64에서 EXC_BAD_ACCESS 충돌이 발생합니다.

- (void)makeObjectsPerformSelector: (SEL)selector withArguments: (void*)arg1, ... 
{ 

    va_list argList; 

    NSArray* currObjects = [NSArray arrayWithArray: self]; 
    for (id object in currObjects) 
    { 
     if ([object respondsToSelector: selector]) 
     { 
      NSMethodSignature* signature = [[object class] instanceMethodSignatureForSelector: selector]; 

      NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: signature]; 
      invocation.selector = selector; 
      invocation.target = object; 

      if (arg1 != nil) 
      { 
       va_start(argList, arg1); 

       char* arg = arg1; 

       for (int i = 2; i < signature.numberOfArguments; i++) 
       { 
        const char* type = [signature getArgumentTypeAtIndex: i]; 
        NSUInteger size, align; 
        NSGetSizeAndAlignment(type, &size, &align); 
        NSUInteger mod = (NSUInteger) arg % align; 

        if (mod != 0) 
         arg += (align - mod); 

        [invocation setArgument: arg 
            atIndex: i]; 

        arg = (i == 2) ? (char*) argList : (arg + size); 
       } 

       va_end(argList); 
      } 

      [invocation retainArguments]; 
      [invocation invoke]; 
     } 
    } 
} 

인수와 관련하여 문제가있는 것 같습니다.

+0

코드를 호출하고 충돌하는 코드 샘플을 제공 할 수 있습니까? –

+0

주어진 코드는 NSArray 클래스의 카테고리이며 배열의 각 객체가 다중 인수로 선택자를 수행 할 수있는 기능을 제공합니다. 배열의 각 객체는 "다중 리스너"디자인 패턴에 필요한 것처럼 리스너 (델리게이트)입니다. 예를 들어 서버에서 응답 한 후에는 각 수신기에서 선택기를 수행해야합니다. 서버 성공 콜백에있는 호출은 다음과 같습니다 : '[self.listeners makeObjectsPerformSelector : @selector (serverManager : didLikeVideo : withError :) withArguments : self, operation.video, operation.error, nil]; ' – abagmut

+0

그래서, 안전하지 않은 유형 변환이 없으면 업데이트 된 답변을 확인하십시오. 메모리에서 인수 찾기와 함께 복잡한 트릭을하는 이유를 이해할 수 없습니다. –

답변

5

이것은 같은 목적을 가지고 있습니다.

+ (void)callSelectorWithVarArgs:(SEL)selector onTarget:(id)target onThread:(id)thread wait:(BOOL)wait, ... 
{ 
    NSMethodSignature *aSignature = [[target class] instanceMethodSignatureForSelector:selector]; 

    if (aSignature) 
    { 
     NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature:aSignature]; 
     void *  arg; 
     int   index = 2; 

     [anInvocation setSelector:selector]; 
     [anInvocation setTarget:target]; 

     va_list  args; 
     va_start(args, wait); 

     do 
     { 
      arg = va_arg(args, void *); 
      if (arg) 
      { 
       [anInvocation setArgument:arg atIndex:index++]; 
      } 
     } 
     while (arg); 

     va_end(args); 

     [anInvocation retainArguments]; 

     if (thread == nil) 
     { 
      [anInvocation performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:wait]; 
     } 
     else 
     { 
      [anInvocation performSelector:@selector(invoke) onThread:thread withObject:nil waitUntilDone:wait]; 
     } 
    } 
} 

이 코드는 필요할 때 유형 변환을 수행 할 때 잠재적으로 안전하지 않을 수 있습니다. 호출 된 메서드가 내 callSelectorWithVarArgs:onTarget:onThread:wait:에 전달 된 더 긴 인수를 가질 때 (예 : 호출 된 메서드가 NS64nt의 64 비트 인 NSUInteger를 받지만 64 비트의 읽기를 유발하는 int (32 비트는 팔과 arm64))을 전달합니다. 32 비트 변수의 시작 주소 및 데이터의 휴지통). 어쨌든, 구현은 잠재적으로 위험합니다. 랩 된 메소드에 전달 된 모든 인수를 호출 된 메소드의 인수와 동일한 유형으로 취급합니다.

이 작동 수정 된 코드입니다 : 당신은 INT를 사용하는

- (void)makeObjectsPerformSelector:(SEL)selector withArguments: (void*)arg1, ... 
{ 
    NSArray* currObjects = [NSArray arrayWithArray: self]; 
    for (id object in currObjects) 
    { 
     if ([object respondsToSelector: selector]) 
     { 
      NSMethodSignature* signature = [[object class] instanceMethodSignatureForSelector: selector]; 

      NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: signature]; 
      invocation.selector = selector; 
      invocation.target = object; 

      [invocation setArgument:&arg1 atIndex:2]; 

      NSInteger index = 3; 
      void *  arg; 

      va_list  args; 
      va_start(args, arg1); 

      do 
      { 
       arg = va_arg(args, void *); 
       if (arg) 
       { 
        [invocation setArgument:&arg atIndex:index++]; 
       } 
      } 
      while (arg); 

      va_end(args); 

      [invocation retainArguments]; 
      [invocation invoke]; 
     } 
    } 
} 
+0

수정 된 코드는 64 비트에서 정상적으로 작동하지만 여기서는 [NSInvocation setArgument : atIndex :]'경계에 도달했기 때문에 32에서 충돌이 발생했습니다. 그래서 루프를 대체했습니다 : do { arg = va_arg (args, void *); if (arg) { [invocation setArgument : & arg atIndex : index ++]; } } while (arg); (NSUInteger i = index; i abagmut

+1

어떻게 경계에 도달 할 수 있습니까? 2,147,483,647 개의 인자를 가진 메소드를 호출합니까? –

+0

'arg = va_arg (args, void *);''signature.numberOfArguments'를 넘는 인덱스에 대해서는 포인터를 인식하지 못하고'nil'이 아닙니다. – abagmut

4

이 코드는 다른 인수의 레이아웃에 대해 va_list에 있고, arm64에서는 작동하지 않는 것으로 가정하고 있습니다.

예를 들어, va_list의 인수 레이아웃에 의존하는 다른 문제를 해결하기 위해 other tricks (32 비트에서 작동하지만 64 비트에서는 작동하지 않음)을 볼 수 있습니다 .

va_list에서 인수에 액세스하는 유일한 이식 방법은 va_arg이지만, 컴파일 타임에 고정 유형이 필요합니다.

1

하고 당신이 64 비트에서 32 비트하지만 충돌에 벌금을 실행 말한다. 반복을 위해 NSInteger 또는 NSUInteger로 전환하십시오. 귀하의 문제를 해결할 것 같아

+0

저는 메서드에 2,147,483,647 개 미만의 인수가 있으므로, 그렇지 않습니다. –

+0

int의 상위 32 비트가 64 비트 아키텍처에서 가비지가 아니라고 말할 수 있습니까? 나는 그렇게 생각하지 않는다. –

+0

미안, 뭐라구? 32 비트 변수를 64 비트 변수에 할당 할 때 타입 변환이 수행됩니다. –

1

두 번 이상 인수 목록을 사용하고 있습니다. 그렇게하는 것은 정의되지 않은 동작입니다. 대신 va_copy을 사용하여이 문제를 해결할 수 있습니다.

va_start(argList, arg1)을 바깥 쪽 for 바깥으로 이동하고 다음을 사용하여 인수 목록의 사본을 만듭니다. va_list copyArgList; va_copy(copyArgList, argList);. 그런 다음 복사 된 인수 목록을 정상적으로 사용하십시오.

자세한 내용은 대한 va_copy

0

나는 당신이이 방법에서 탈피를 살펴보고 변수 인수를 가로 지르는 유일한 안전 메커니즘이다 va_arg에 따라 안전 메커니즘에 일을 코딩 할 필요가 있다고 생각합니다. @Nikita가 게시 한 라인을 따라 무엇인가.

현재 접근법을 계속하려면 각 아키텍처에 대한 iOS 호출 규칙을 알아야합니다. 여기에서 ARM64 협약을 찾을 수 있습니다 : https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html

초기 표정과는 달리 명확하게 직선적이지 않고 가변 함수는 일반 호출 규칙과 다릅니다.