3

여기의 질문은 교육적인 내용입니다. 나는 한 시간 전에 이것을 생각하기 시작했다. 은 레고 블록 (어리석은 것, 나는 알고있다) 주위에 튀기고있다.블록 및 메시징

블록은 내가 이해 한 것으로부터 스택에 만들어진 개체입니다.

하자가 A 말은 우리가 할 수있는 것을 의미 객체입니다 : 블록 객체 인 경우

[A message]; 

을 그 바탕으로, 우리는 또한 할 수있는 :

[block message]; 

내가 수정 있습니까 ?

그리고 런타임이 있음을 볼 때, 그것은 부를 것이다 :

objc_msgSend(block, @selector(message), nil); 

그래서 제 질문은, 어떻게 우리가 블록 메시지를 보낼 수 있습니까?

그리고 이것이 가능하다면 블록을 인자로하는 메시지를 블록으로 보내는 것도 가능할 것이라고 생각합니다. 블록의 유사한 서명 void (^)(void)을 가지고 있기 때문에, 우리는 심지어뿐만 아니라 메시지 (SEL)와 같은 블록을 만들 수있는 의미

block(); 

합니까를 우리가 수행하여 블록을 호출 할 수 있다면

그리고, 방법?

이 가능할 것이다 경우 때문에, 그 다음은 정말 나를 놀라게 것입니다 :

objc_msgSend(block, @selector(block), block); 

나 :

objc_msgSend(block1, @selector(block2), block3); 

내가 내 상상력 야생 비트를 실행하고 나의 이해는 것을하지 않는 희망 여기서 벗어나지 마라.

+0

블록 리터럴에는 'invoke()'함수 포인터가 있습니다. 그들은 호출을위한 objective-c 인터페이스를 필요로하지 않습니다. 즉, 잘 정의 된 호출 메소드의 경우 단순히 인수가 다른 레이아웃 (블럭이'void (^) (void) '일뿐만 아니라)이있는 각 블록 리터럴에 대해 NSBlock의 고유 한 하위 클래스를 만들어야합니다. – CodaFi

+0

이 방법은 컴파일러가 어떤 함수를 생성해야하는지 완벽하게 제어 할 수 있으며 리터럴이 사용될 때 블록이 스택에 간단히 만들어지고 해당 함수 포인터가 할당됩니다. – CodaFi

+0

@CodaFi 미안하지만, 좀 더 자세히 말해 줄 수 있고, 내가 조금 잃어 버렸기 때문에 샘플, 추론 등 좀 더 세부적인 내용을 어떻게 성취 할 수 있는지 보여줄 수 있습니까? 감사. – Unheilig

답변

10

블록은 저장 및 참조 목적으로 만 사용되는 개체입니다. 객체를 객체로 만들면 블록을 retain/release 할 수 있으므로 배열이나 다른 컬렉션 클래스로 밀어 넣을 수 있습니다. 또한 copy에 응답합니다.

그게 전부입니다. 심지어 스택에서 시작되는 블록은 대부분 컴파일러 구현 세부 사항입니다.

블록의 코드가 호출되면 objc_msgSend()을 통해 수행되지 않습니다. 당신이 블록 런타임과 LLVM 컴파일러의 소스를 읽는다면, 당신은 그것을 찾을 것 :

  • 을 블록 정말 캡처 된 데이터에 대한 설명을 포함하는 C 구조 (그래서입니다 그것은 청소) 및 함수에 대한 포인터를 할 수 있습니다 - 코드의 덩어리로는 - 그 블록

  • 블록 함수는 표준 C 함수의 실행 부분이다 첫 번째 인수 해야 항상 블록에 대한 참조가되어야합니다. 인수 목록의 나머지 부분은 임의적이며 오래된 C 함수 또는 Objective-C 메서드와 동일하게 작동합니다.

그래서, objc_msgSend()에 수동 통화는 다른 임의 ObjC 개체처럼 블록을 치료하고, 따라서 블록의 코드를 호출하지 않으며, 그것은 한 경우 (그리고 할 수있는 SPI가있다 이것은 메소드에서 ... 그러나 그것을 사용하지 마십시오.) 완전히 제어 할 수있는 인수 목록을 전달할 수 있습니까?


하나는 제외하고 관련성이 있습니다.

imp_implementationWithBlock()은 블록 참조를 가져 와서 Objective-C 클래스 (class_addMethod() 참조)에 꽂을 수있는 IMP를 반환하므로 메서드가 호출 될 때 블록이 호출됩니다.

imp_implementationWithBlock()의 ​​구현은 objective-c 메서드의 호출 사이트 레이아웃을 블록과 비교하여 사용합니다.

blockFunc(blockRef, ...) 

과 ObjC 방법은 항상 : 블록은 항상

methodFunc(selfRef, SEL, ...) 

우리는() 항상 첫 번째 블록 매개 변수와 메소드에 대상 객체를 취할 차단하려면 imp_implementationWithBlock를 원하기 때문에 (즉,

- slides the self reference into the slot for the selector (i.e. arg 0 -> arg 1) 
- finds the implementing block puts the pointer to that block into arg 0 
- JMPs to the block's implementation pointer 

는 실행 B 발견 : 자기)는 imp_implementationWithBlock)는() (objc_msgSend 통해 (라는 것을 나토 함수를 리턴) lock 비트는 다소 흥미롭지 만이 질문과 관련이 없습니다 (지옥, imp_implementationWithBlock()은 약간 관련이 없지만 관심의 대상 일 수 있음). 응답


감사합니다. 확실히 눈을 뜨게합니다.obchc_msgSend()는 블록이 일반적인 객체 - 계층 구조의 일부가 아니기 때문에 블록 호출이 완료되지 않았 음을 알립니다. (NSBlock을 언급 한 코다의 은 NSBlock이 내가 이해 한 것을 반박하는 것으로 보입니다. 그것을 대상으로 삼는다. 내 이해가 아직까지 떨어져 있다면 나를 찔러 야합니다. 나는 다음에 대해 더 많이 듣고 싶습니다. 1 : SPI와 방법 (방법)을 호출하는 방법. 2 : 기본 메커니즘 : 자체 참조를 슬롯으로 밀어 넣습니다. 3 : 구현하는 블록을 찾고 해당 블록에 대한 포인터를 arg 0에 넣습니다. 시간이 있으면 세부 사항에 대해 좀 더 자세히 공유하고 작성할 수 있습니다. I 이 모든 것이 매우 매력적입니다. 미리 감사드립니다.

블록 자체는 단지 표준 Objective-C 개체입니다. 블록 인스턴스는 실행 가능한 코드, 캡처 된 상태, 스택에서 힙 (요청 된 경우)으로 상태를 복사하고 블록의 파기 된 상태를 정리하는 데 사용되는 일부 도우미에 대한 포인터를 포함합니다.

블록의 실행 코드는 메소드처럼 호출되지 않습니다. 블록은 retain, release, copy 등의 다른 메서드를 가지고 있습니다. 다른 메서드와 마찬가지로 직접 호출 할 수 있지만 실행 가능 코드는 공개적으로 해당 메서드 중 하나가 아닙니다.

SPI는 특별한 작업을 수행하지 않습니다. 인수를 취하지 않는 블록에서만 작동하며 단순히 block()을 수행하는 것 이상입니다.

전체 인수 슬라이드가 어떻게 작동하는지 (그리고 블록까지 꼬리 호출을 수행하는 방법) 알고 싶다면 this 또는 this을 읽는 것이 좋습니다. 또한 블록 런타임, objc 런타임 및 llvm의 소스를 모두 사용할 수 있습니다.

여기에는 IMP가 블록을 잡고 arg0로 밀어 넣는 재미있는 비트가 포함되어 있습니다.

+0

응답 해 주셔서 감사합니다. 확실히 눈을 뜨게합니다. 블록 호출에 대한 부분은'objc_msgSend()'를 통해 수행되지 않습니다. 왜냐하면 블록은 일반적인 객체 - 계층 구조의 일부가 아니기 때문입니다. (NSBlock에 대한 코다의 언급은 NSBlock이 지금까지 이해 한 것을 반박하는 것으로 보입니다. '그것을 대상으로 삼는다. 내 이해가 아직 멀어지면 자유롭게 나를 찔러 봐라. 나는 다음에 대해 더 많이 듣고 싶다 : – Unheilig

+0

(계속) ** 1 : ** SPI와 그 방법을 호출하는 방법 (방법). ** 2 : ** 자체 메커니즘을 슬롯에 밀어 넣는 기본 메커니즘. ** 3 : ** 구현 블록을 찾아 해당 블록에 대한 포인터를 arg 0에 넣습니다. 자세한 내용을 공유하고 작성할 시간이 있다면 나는 모두 귀입니다. 나는이 모든 것을 매우 매력적이라고 ​​생각한다. 미리 감사드립니다. – Unheilig

+0

방금 ​​당신이 플레이 스테이션 용 무언가없는 자동 사격 시스템의 발명가라는 것을 깨달았습니다 ... – Unheilig

3

예, 블록은 개체입니다. 네, 메시지를 보낼 수 있다는 의미입니다.

그러나 어떤 메시지가 블록에 응답한다고 생각하십니까? 메모리 관리 메시지 retain, releasecopy 이외에 블록이 지원하는 메시지는 알 수 없습니다. 따라서 블록에 임의의 메시지를 보내면 "선택자를 인식하지 못함"예외가 발생할 가능성이 있습니다 (인터페이스를 알지 못하는 임의의 객체에 임의의 메시지를 보낸 경우와 동일한) .

블록 호출은 메시지 전송과 다른 메커니즘을 통해 이루어지며 컴파일러가 구현 한 마법이며 그렇지 않은 경우 프로그래머에게 노출되지 않습니다.

블록 및 선택기는 매우 다릅니다. (셀렉터는 메서드 이름의 문자열 인 인턴 된 문자열입니다.) 블록과 IMP (메서드를 구현하는 함수)는 다소 유사합니다. 그러나 수신기가 수신자 (self)를 수신하고 선택기가 특수 매개 변수로 호출된다는 점에서 블록이 구현되지 않은 반면 (블록을 ​​구현하는 기능은 숨겨진 매개 변수로만 블록 자체를 수신하므로 프로그래머가 액세스 할 수 없음)).

+0

_ "셀렉터를 인식하지 못합니다"_는 블록에 메시지를 보내는 것이 "합법적"이라고 알려주며 단지 'SEL'을 인식하지 못합니다. 이것을 가능하게하는 방법이 있습니까? "함수 구현 메소드"와 "블록을 구현하는 함수는 블록 자체를 숨겨진 매개 변수로만받습니다"라는 부분에 대해 더 자세히 설명해 주시겠습니까? 하지만 말했듯이, 프로그래머는 접근 할 수 없지만 적어도 개념적으로는 가능합니까? 그래서 블록이 호출 될 때, 런타임은'objc_msgSend'를 사용하지 않을 것입니다. 나는 실수로 그것을 생각했을 것입니다. ..? 감사. – Unheilig

+0

@Unheilig : "내가 잘못 생각한 것처럼 런타임에서 objc_msgSend를 사용하지 않을 것입니다 ...?" 글쎄, 그것은 프로그래머가 밑에 무엇을 사용하는지 알지 못한다. 그러나 우리가 아는 한 가지는 'nil'에 전달되는 메시지가 충돌하지 않지만 'nil'블록 호출이 중단된다는 것입니다. "부분을 구현하는 함수에 대해 좀 더 알려주시겠습니까?" "methodForSelector :'를 사용하여 메소드 (IMP)를 구현하는 함수에 대한 포인터를 얻을 수 있습니다. 처음에는'self'와'_cmd'라는 두 개의 함축적 인 매개 변수를 취하고 "regular"매개 변수가 뒤에 오는 C 함수입니다. – newacct

+0

@Unheilig : 블록을 구현하는 함수에 접근 할 수 없지만 런타임 함수'imp_implementationWithBlock()'을 사용하여 특정 서명이있는 블록을 IMP로 변환 할 수 있습니다. – newacct