2014-11-25 3 views
1

에 목표 - C 블록을 전달하려면 어떻게 성공적으로 매개 변수로 블록을 필요로하는 메소드를 호출하여 다음과 같은 기능이 있습니다 : 수동 objc_msgSend

OBJC_EXPORT 
void testsendmsg(void *block) 
{ 
    [NSEvent addLocalMonitorForEventsMatchingMask:1024 handler:block]; 
} 

가 지금은 objc_msgSend를 사용하여이 메서드를 호출하는 것을 시도하고있다. 위 함수의 디스 어셈블리를 보면 블록 앞에 파라미터로 추가 "0"을 전달해야한다는 것을 알았습니다. 그로 인해 다음 코드가 작동하는 것으로 보입니다.

OBJC_EXPORT 
void testsendmsg(void *block) 
{ 
    objc_msgSend(objc_getClass("NSEvent"), sel_getUid("addLocalMonitorForEventsMatchingMask:handler:"), 1024, 0, block); 
} 

하지만이 "0"의 의미는 무엇입니까? 항상 "0"입니까 아니면 다른 값을 전달해야합니까?

궁극적 인 목표는 addLocalMonitorForEventsMatchingMask:handler (델파이)입니다. 따라서 (void *block)을 다른 것으로 변경하거나 handler:^(NSEvent *event){ return nil; };과 같은 공식 블록 구문을 사용하는 것은 나를위한 옵션이 아닙니다.

+0

RTL은 Macapi.OCBlocks.pas에서이 작업을 수행 할 수 있습니다. 장치가 제공하는 옵션을 사용하거나 GetObjCBlock/FreeBlock 함수를 사용하여 _objcblockcallbackallpointer 프로 시저를 제공 할 수 있습니다. – Giel

+0

이미 해당 장치를 살펴 보았습니다. 그러나 해당 코드를 올바르게 읽으면 GetObjCBlock은 {$ IFDEF CPUARM}이 정의 된 경우에만 구현됩니다. –

답변

1

우선 유형이 잘못되어 objc_msgSend을 직접 호출해서는 안됩니다. 그것은 실제 기능이 아닙니다. 이것은 목적지 IMP로 실행을 전달하는 트램펄린 (trampoline)이기 때문에 목적지 IMP의 유형의 기능을 수행합니다. 따라서 호출하기 전에 대상 IMP의 함수 유형에 먼저 캐스트해야하므로 컴파일러는 대상 IMP에 대해 올바른 호출 규칙을 사용합니다. 이 코드는 오브젝티브 C에 있다면 당신이 그것을 부를 것이다 방법입니다

id (*f)(id, SEL, NSEventMask, NSEvent *(^)(NSEvent *)) = 
    (id (*)(id, SEL, NSEventMask, NSEvent *(^)(NSEvent *)))objc_msgSend; 
id result = f([NSEvent class], 
       @selector(addLocalMonitorForEventsMatchingMask:handler:), 
       NSKeyDown, block); 

당신이 목표 - C 나 또는 모든 코코아 변수 (C의 확장) 블록 구문을 피할 필요가 보인다. 괜찮아. 열거 형 NSEventMask을 사용할 수 있는지 여부는 잘 모르겠지만 가정 할 수 없다고 가정하면 from the documentation의 기본 유형은 unsigned long long입니다. 그래서 당신은 할 것이다

id (*f)(id, SEL, unsigned long long, void *) = 
    (id (*)(id, SEL, unsigned long long, void *))objc_msgSend; 
id result = f(objc_getClass("NSEvent"), 
       sel_getUid("addLocalMonitorForEventsMatchingMask:handler:"), 
       1024, block); 

그것은 또한 여분의 "0"이 있다고 생각하는 이유를 설명한다; unsigned long long은 두 개의 프로세서 단어를 사용합니다.

+0

트릭은 실제로 NSEventMask가'unsigned long long'이어야한다는 트릭입니다. 그래서 최종 솔루션은 다음 '형 NSEventMask = UINT64;' 'R = objc_msgSend (objc_getClass ('NSEvent ') sel_getUid ('addLocalMonitorForEventsMatchingMask : 핸들러 ') NSEventMask (NSKeyDownMask) 블록)' –

+0

@ 세바스찬 Z : 당신은 여전히'objc_msgSend'를 직접 호출해서는 안됩니다. 'objc_msgSend'의 선언은 가짜입니다 - varargs를 취하기로 선언되었지만 실제로 일어나는 것은 아닙니다. 목적지 IMP와 같은 역할을합니다. 가변 인수에 대한 호출 규칙은 상응하는 다중 인수 함수와 항상 동일한 것은 아닙니다. 예를 들어, varargs에서'float'은'double'으로 승격되고'char'와'short'는'int'로 승격됩니다. 보통 인수의 경우 아키텍쳐에 따라 다르거 나 그렇지 않을 수 있습니다. – newacct

+0

@SebastianZ : 그리고 구조체 반환의 경우,'objc_msgSend_stret'를 사용하면 호출 규칙이 완전히 다릅니다 (struct는 구조체에 대한 포인터를 인수로 전달하여 작동합니다). 올바른 함수 유형으로 형변환하면 올바르게 작동합니다. 이 예제에서는 varargs 타입을 사용하여 작업 할 수 있습니다. 왜냐하면'int'와 포인터와'long long's가이 아키텍처에서 두 가지 경우 모두 같은 방식으로 전달되기 때문입니다. 그러나 당신은 그것에 의존하지 말아야합니다. 아키텍처 또는 다른 유형을 사용하면 작동하지 않을 수 있습니다. – newacct