일단 위의 __weak나 __block을 사용하는 범위는 block coding을 하는 부분으로 작게만 생각해보도록 합시다.
Block이란?
Objective-C에서 closure의 개념을 도입하기 위해서 만들어진 블럭 코드 부분입니다. 실제로는 이부분은 선언될 경우에 컴파일러에 의해서 __block_literal 이라는 구조체로 만들어진다고 합니다.
이 구조체 안에는 isa정보가 들어가 있어서 Objective-C의 특성을 고스란히 가지게 된다고 합니다.
일단 만들어지게 되면 힙에 생성이 되는게 아니라 스택에서 생성이 된다고 하니 조금 의아하긴 하네요.
__block 키워드
블럭코드를 만들때 어떤식으로 저장해두고 있을지를 정하게 되는 스토리지 타입 변경자라고 합니다.
객체일경우에는 자동으로 retain을 하게 되고,
객체이고 __block 키워드가 있을 경우에는 retain을 하지 않음... 당연히?
프리미티브타입일 경우에는 const 형태로 value copy가 되고, 프리미티브타입이고 __block 키워드가 붙었을 경우에는 reference형태로 전달되게 된다합니다. 그런데 이 키워드가 중요한 의미는 다음과 같은데요.
1. 블럭내부에서 값의 변경점.
1
2
3
4
5
6
7
8
|
int x = 1;
void (^example)(int) = ^(int y) {
x = x + y; // Error. Variable is not assignable (missing __block type specifier)
NSLog(@"x = %d, y = %d", x, y);
};
example(2);
|
cs |
일단 이렇게 코딩 되면 당연히 값이 변경이 안됩니다. 이유는 위에 적은거처럼 값을 만들때 const 형태로 만들어버리기 때문에 변경을 할 수가 없습니다.
1
2
3
4
5
6
7
8
|
__block int x = 1;
void (^example)(int) = ^(int y) {
x = x + y;
NSLog(@"x = %d, y = %d", x, y); // success
};
example(2);
|
cs |
이렇게 만들어버리면 잘 동작을 하겠죠.
2. block안에서 상호참조가 발생하는 경우솔찍히 그냥 대충 개발하면 이런거 신경쓰지 않아도 됩니다. 왜냐면 요즘은 폰이 좋아져서 웬만하면 어플이 죽는 문제는 발생하지 않거든요.
그렇지만 우리는 대충 짜는 개발자가 아니기에 이런걸 넘어가면 안됩니다.
* 순환참조
만약에 블럭안에서 self를 참조하는 상황이 발생합니다.
예를 들어 [self test]를 블럭안에서 호출하게 되면 self를 사용하게 되면서, 즉 UIViewController의 retain이 하나 증가하게 됩니다.
이렇게 되는 상황에서 self의 retain이 영원히 0이 되지 않아서 메모리에서 회수가 안되는 부분이 발생하게 되는데요.
이것을 순환참조라고 합니다.
그래서 이렇게 쓰지 말고요..
1
2
3
4
5
|
void (^example)(int) = ^(int y) {
[self test];
};
example(2);
|
cs |
이렇게 쓰도록 합니다.
1
2
3
4
5
6
|
__weak typeof(self) weakSelf = self;
void (^example)(int) = ^(int y) {
[weakSelf test];
};
example(2);
|
cs |
자... 마지막으로 블럭을 프로퍼티를 사용할 경우에는 copy를 사용하도록 하는데요.
이유는 블럭 내에서 블럭이 선언될 당시의 스택프레임에 참조하는 변수가 없다면 상관이 없지만 존재하게 된다면 아픔을 느끼게 되기 때문입니다.
1
|
@property (copy) (^void)exampleBlock(void)
|
cs |
이렇게 합시다.