Skip to content

Instantly share code, notes, and snippets.

@linearhw
Created December 12, 2017 06:44
Show Gist options
  • Save linearhw/619a4b5f09c73c7c48d2785cdc6b864e to your computer and use it in GitHub Desktop.
Save linearhw/619a4b5f09c73c7c48d2785cdc6b864e to your computer and use it in GitHub Desktop.
Effective Objective-C 37

1) 블록이란?

  • 블록은 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

2) 블록의 capture 기능

블록은 블록이 정의된 범위를 capture 할 수 있다.

  • capture란? 복사하는 것. 단 포인터를 복사하는 것이지 객체 자체를 복사하는 게 아님
    그리고 블록은 자신이 capture 한 어떠한 변수도 변경할 수 없다.
    앞에 __block 이 붙은 변수만 변경 가능.

블록은 객체 타입의 변수를 잡았을 때 암묵적으로 그 객체를 retain 한다.
블록 자체가 release 될 때 블록이 capture 한 객체도 release 된다.

블록이 인스턴스 변수를 capture 할 경우 self 변수 또한 암묵적으로 capture 된다.

3) 블록의 메모리

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 되지 않는다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment