-
블록은 Objective-C 에서 closure라는 abstract concept 를 구현한 것.
closure란? function 을 environment 와 함께 저장하는 record
일반적인 객체라고 생각하면 된다. isa 포인터도 가진다. 단순한 함수 포인터를 대신하는 것. -
블록을 실제로 사용하는 방법
return_type ^(block_name)(params) = ^(params) {
return_type;
}
이 외에도 많이 있다
https://stackoverflow.com/questions/9201514/block-declaration-syntax-list
블록은 블록이 정의된 범위를 capture 할 수 있다.
- capture란? 복사하는 것. 단 포인터를 복사하는 것이지 객체 자체를 복사하는 게 아님
그리고 블록은 자신이 capture 한 어떠한 변수도 변경할 수 없다.
앞에 __block 이 붙은 변수만 변경 가능.
블록은 객체 타입의 변수를 잡았을 때 암묵적으로 그 객체를 retain 한다.
블록 자체가 release 될 때 블록이 capture 한 객체도 release 된다.
블록이 인스턴스 변수를 capture 할 경우 self 변수 또한 암묵적으로 capture 된다.
https://www.solstice.com/blog/blocks-and-memory-management-stack-vs-heap
-
스택 영역: thread 는 각각 하나의 stack 을 갖는다. 그리고 thread 들은 힙 영역을 통해 정보를 공유한다.
코드 상에서 new scope 안으로 들어갈 때 ( = { } 를 만났을 때) stack space 를 새로 생성하고
해당 scope 가 끝나고 나면 그 scope 에서 사용되었던 stack space 를 정리한다.
따라서 스택에 있는 객체는 retain 할 수 없다. scope 가 끝나면 사라지기 때문. -
힙 영역: alloc 이나 new 를 사용했을 때 dynamic 하게 할당되는 곳.
application 전체에 대해 unique 한 공간.
블록이 정의되면 메모리는 스택 영역에 할당된다. 왜? 얘는 결국 포인터니까.
그래서 이런 코드는 위험하다.
void (^block)();
if (/* some condition */) {
block = ^{
NSLog(@"Block A");
}; // if 의 scope 가 끝나면 바로 stack space 가 정리된다.
} else {
block = ^{
NSLog(@"Block B");
}; // else 의 scope가 끝나면 바로 stack space 가 정리된다.
}
block();
스택에 있는 객체를 retain 하고 싶으면? heap 에 넣어줘야 한다.
그러려면? copy method 를 사용해야 한다.
if (/* some condition */) {
block = [^{
NSLog(@"Block A");
} copy]; // 요렇게
}
근데 만약 scope 의 변수를 하나도 capture 하지 않는 블록이면?
void (^block)() = ^{
NSLog(@"This is a block");
}
이 경우 블록이 차지하는 메모리 영역이 정확히 얼마일지 컴파일 타임에 완전히 알 수 있다. 굳이 heap 에 dynamic 하게 할당할 필요가 없다.
그래서 이러한 경우 전역 변수 같은 취급을 한다.
스택 대신 전역 메모리(?)에 생성하고, 복사해도 아무런 동작을 하지 않으며, 프로그램이 죽을 때까지 dealloc 되지 않는다.