2017-12-20 8 views
1

Obj-C에서 메소드를 시도하고 있지만 순수한 C 함수를 전달하려고합니다. 이것은 내가 어떻게 든 selector를 할당하거나 수동으로 objc_method 구조체를 구축해야한다는 것을 의미합니다. 어쩌면 어떻게 든 NSInvocation을 활용할 수 있을까요?Objective-C에서 콜렉터없이 C 함수의 selector를 할당하십시오.

Obj-C는 C의 엄격한 수퍼 세트이므로 완벽하게 호환됩니다. 내가 지금가는 것을

:

main.m : 나는 (내가 피할 싶습니다) @implementation 내부의 C 기능을 퍼팅 시도하고 심지어 다음은 작동하지 않았다

#include.... 

CFStringRef strRet(void) { 
    return CFSTR("retString"); 
} 

int main(int argc, const char * argv[]) { 
    @autoreleasepool { 
     SEL _strRet = sel_registerName("strRet"); 
     //I also tried: SEL _strRet = NSSelectorFromString(@"strRet"); 

     Class bundle = objc_getClass("NSBundle"); 

     method_exchangeImplementations(
      class_getInstanceMethod(bundle, sel_registerName("anySelector")), 
      class_getInstanceMethod(bundle, sel_registerName("_strRet") 
     ); 

.

+0

잘못 이해하면 정확한 정보를 제공해주세요. 당신은 순수한 C 함수를 전달하고 싶습니까 ?? 코드 스 니펫에 C 함수가 표시되지 않습니다. 좀 더 많은 코드를 쿼리를 수정할 수 있습니까 ?? –

답변

2

C 기능을 사용할 수 없습니다. 그 자체는입니다. Swizzling은 메서드 설명 (런타임 함수에 의해 Method 유형으로 표시됨)을 통과하는 메서드 조회에 기반하며 C 함수에는 메서드 설명이 없습니다.

그러나 메소드의 구현은 C 함수에 불과합니다. 이러한 C 함수는 메서드가 호출되는 개체 (Objective-C 암시 적 매개 변수 self)와 선택기 (Objective-C 암시 적 매개 변수 _cmd) 인 최소 두 개의 인수를 취해야합니다. 메소드를 바꿀 때 대체 구현 인 C 함수는 원본과 정확히 같은 유형을 가져야합니다. 즉, 두 개의 암시적인 인수가 있어야합니다. 따라서 strRet()은 적합하지 않으므로 다음과 같이 변경해야합니다.

,

는 가장 쉬운 방법은 본문 당신의 "순수"C의 함수 인 방법을 정의하는 것입니다
  1. , 그때 제대로 상속을 처리하기 위해 돌보는 (권장되는 방법을 스위 즐링 (swizzle) :

    CFStringRef strRet(NSObject *self, CMD sel, void) 
    { 
        return CFSTR("retString"); 
    } 
    

    그래서 당신은 세 가지 선택 사항이 있습니다 this answer 참조).

  2. 당신은 정말 C의 기능과 다음 방법의 원래 구현을 호출 할 필요가 없습니다 않는 C 함수를 작성하려면 :

    (가) 당신은 하나의로 C의 기능을 변환 할 필요가있는 메소드 구현으로 사용할 수 있습니다. 다음과 같이 할 수 있습니다 :

    • C 함수의 소스를 작성하고 있다면 위와 같이 두 개의 암시적인 인수를 취하도록 정의하십시오. 이 함수의 주소를 가져와 IMP으로 캐스팅하십시오. 아래에서 사용할 수있는 적절한 유형의 C 함수 포인터는 typedef입니다.
    • 당신이 중 하나를 수행 할 수 있습니다 정의는 다음 변경할 수 없습니다 C 함수를 사용하는 경우 :
      • 는, 여분의 인수를 그들을 무시하고 대상 C 함수를 호출하는 C 래퍼 함수를 ​​작성. 이 래퍼 함수의 주소를 가져와 아래의 사용을 위해 IMP으로 변환하십시오.
      • 블록에서 C 함수 호출을 감싸고 imp_implementationWithBlock()을 사용하여 IMP 값을 생성하십시오. imp_implementationWithBlock()을 사용하는 방법에 대한 설명은 this article을 참조하십시오.

    (b)가있는 생성 된 IMP 값 구현 설정을 사용 method_setImplementation() (a).

  3. 당신이 정말로 C 함수와 는 다음 구현이 당신의 C 함수입니다 클래스에 메서드를 추가해야하는 메소드의 원래 구현을 호출 할 필요가 않는 C 함수를 작성하려면 - 수정/(2)와 같이 래핑 된 다음 추가 된 메소드를 원래 메소드로 아래 (1)과 같이 바꿔 원래 구현을 여전히 메소드로 사용할 수있게하십시오. 방법을 추가하려면 여기 class_addMethod()

HTH

+1

imp_implementationWithBlock()을 사용할 수도 있습니다. 런타임에 메서드로 플러그인 할 수있는 블록에 c 함수를 쉽게 래핑 할 수 있습니다. – bbum

+2

@bbum - Duh, 그걸 잊어 버렸어. 대답을 편집하여 옵션으로 포함 시켰습니다. 감사. – CRD

+0

대단하군요! 자원을 가져 주셔서 감사합니다! – JLegendre

1

키를 사용하는 함수 포인터와 컨텍스트 사이에 매핑하는 메커니즘을 찾는 것입니다. 가장 간단한 방법은 새 함수 포인터를 생성하는 것입니다. imp_implementationWithBlock(), MABlockClosure을 사용하거나 나만의 것을 사용할 수 있습니다.

내가 찾은 새로운 함수 포인터를 만드는 가장 간단한 메커니즘은 전체 함수를 새 주소 공간으로 다시 매핑하는 것입니다. 새로운 결과 주소는 필수 데이터의 키로 사용될 수 있습니다.

#import <mach/mach_init.h> 
#import <mach/vm_map.h> 

void *remap_address(void* address, int page_count) 
{ 
    vm_address_t source_address = (vm_address_t) address; 
    vm_address_t source_page = source_address & ~PAGE_MASK; 

    vm_address_t destination_page = 0; 
    vm_prot_t cur_prot; 
    vm_prot_t max_prot; 
    kern_return_t status = vm_remap(mach_task_self(), 
           &destination_page, 
           PAGE_SIZE*(page_count ? page_count : 4), 
           0, 
           VM_FLAGS_ANYWHERE, 
           mach_task_self(), 
           source_page, 
           FALSE, 
           &cur_prot, 
           &max_prot, 
           VM_INHERIT_NONE); 

    if (status != KERN_SUCCESS) 
    { 
     return NULL; 
    } 

    vm_address_t destination_address = destination_page | (source_address & PAGE_MASK); 

    return (void*) destination_address; 
} 

page_count은 원래 기능을 모두 포함 할 수있을만큼 커야합니다. 또한 더 이상 필요하지 않은 페이지를 처리하고 호출 당 더 많은 메모리가 MABlockClosure보다 많이 걸린다는 점에 유의하십시오.

(iOS에서 테스트 됨)

관련 문제