2012-10-22 8 views
9

iOS 앱의 특정 C 라이브러리에 C 스타일의 콜백을 제공해야합니다. 콜백에는 void *userData 또는 이와 유사한 것이 없습니다. 그래서 나는 한 맥락에서 반복 할 수 없다. 이 문제를 해결하기 위해 글로벌 컨텍스트를 도입하는 것을 피하고 싶습니다. 이상적인 솔루션은 Objective-C 블록입니다.ObjectiveC 블록을 함수 포인터로 래핑하는 방법이 있습니까?

내 질문 : 블록을 함수 포인터로 '캐스트'하거나 랩핑/클로킹하는 방법이 있습니까?

답변

6

기술적으로 블록의 함수 포인터에 액세스 할 수 있습니다. 그러나 그렇게하는 것은 전적으로 안전하지 않으므로 나는 그것을 권장하지 않습니다. 다음 예제를 고려하는 방법을 보려면 :

#import <Foundation/Foundation.h> 

struct Block_layout { 
    void *isa; 
    int flags; 
    int reserved; 
    void (*invoke)(void *, ...); 
    struct Block_descriptor *descriptor; 
}; 

int main(int argc, char *argv[]) { 
    @autoreleasepool { 
     // Block that doesn't take or return anything 
     void(^block)() = ^{ 
      NSLog(@"Howdy %i", argc); 
     }; 

     // Cast to a struct with the same memory layout 
     struct Block_layout *blockStr = (struct Block_layout *)(__bridge void *)block; 

     // Now do same as `block()': 
     blockStr->invoke(blockStr); 




     // Block that takes an int and returns an int 
     int(^returnBlock)(int) = ^int(int a){ 
      return a; 
     }; 

     // Cast to a struct with the same memory layout 
     struct Block_layout *blockStr2 = (struct Block_layout *)(__bridge void *)returnBlock; 

     // Now do same as `returnBlock(argc)': 
     int ret = ((int(*)(void*, int a, ...))(blockStr2->invoke))(blockStr2, argc); 
     NSLog(@"ret = %i", ret); 
    } 
} 

는 실행이 수율 :

우리가 순수하게 block() 직접 그 블록을 실행에서 기대할 수있는 것입니다
Howdy 1 
ret = 1 

. 따라서 함수 포인터로 invoke을 사용할 수 있습니다.

그러나 내가 말했듯이 이는 전적으로 안전하지 않습니다. 이것을 실제로 사용하지 마십시오! 당신은 당신이 요구하는지 무엇을 할 수있는 방법의 쓰기 업을 보려면

,이 체크 아웃 : http://www.mikeash.com/pyblog/friday-qa-2010-02-12-trampolining-blocks-with-mutable-code.html

그것은 당신이해야 할 것이 무엇 단지 좋은 쓰기이야 이것을 작동 시키려면. 안타깝게도, iOS에서는 작동하지 않습니다 (앱의 샌드 박스 내에서 수행 할 수없는 실행 파일로 페이지를 표시해야하기 때문에). 그럼에도 불구하고, 위대한 기사.

+0

제 생각에는 변명의 여지가 없지만 안전하지 않은 이유는 무엇입니까? 구조체가 내부 구조이고 미래에 바뀔 수 있기 때문에 그것이 맞습니까? –

+0

맞습니다 :-). – mattjgalloway

+0

이 문제는 호출 블록에 컨텍스트를 전달할 수 없다는 OP와 달리 호출 블록에 블록을 전달해야한다는 것입니다. – newacct

4

블록에 컨텍스트 정보가 필요하고 콜백이 컨텍스트를 제공하지 않는 경우 대답이 명확하지 않습니다. 블록은 컨텍스트 정보를 어딘가에 저장해야하므로 이러한 블록을 인수가없는 함수 포인터로 캐스트 할 수 없습니다.

신중하게 설계된 전역 변수 접근 방식이이 경우 최상의 솔루션 일 것입니다.

1

MABlockClosure 정확히 할 수 있습니다. 그러나 필요한 것은 무엇이든 과잉 일 수 있습니다.

0

나는 이것이 해결되었지만 이해 관계자에게는 다른 해결책이 있음을 알고 있습니다.

새 주소 공간에 전체 기능을 다시 매핑하십시오. 새로운 결과 주소는 필수 데이터의 키로 사용될 수 있습니다.

#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; 
} 

더 이상 필요하지 않은 페이지를 처리하고 MABlockClosure보다 호출 당 더 많은 메모리를 필요 점에 유의해야합니다.

(iOS에서 테스트 됨)

관련 문제