“__block”키워드는 무엇을 의미합니까?


446

__blockObjective-C 의 키워드는 정확히 무엇을 의미합니까? 블록 내에서 변수를 수정할 수 있다는 것을 알고 있지만 알고 싶습니다 ...

  1. 컴파일러에게 정확히 무엇을 알려줍니까?
  2. 다른 일이 있습니까?
  3. 그게 다라면 왜 처음에 필요한가요?
  4. 어디서나 문서에 있습니까? (찾을 수 없습니다).

3
here 및 "블록 및 변수"섹션을 확인 하십시오 .


1
@ 코드 원숭이 : 나는 일반적으로 구문이 아니라 키워드에 대해 구체적으로 묻고있었습니다. 따라서 실제로 중복이라고 생각하지 마십시오.
mjisrawi

3
@ 코드 원숭이 : 아니오, 이것은 복제본이 아닙니다. 당신이 언급 한 질문은 전혀 이야기하지 않습니다 __block.
DarkDust

3
그리고 누군가 Objective-C가 어떻게 __blockSwift로 번역해야하는지 궁금하다면 : Swift의 클로저는 Objective-C의 블록과 비슷한 캡처 의미를 갖지만 한 가지 핵심적인 방식이 다릅니다. 변수는 복사되지 않고 변경 가능합니다. 즉, Objective-C에서 __block의 동작은 Swift의 변수에 대한 기본 동작입니다.” Apple의 책 : Cocoa 및 Objective-C와 함께 Swift 사용 (Swift 2.2).
Jari Keinänen

답변:


543

컴파일러는 해당 변수로 표시된 변수가 블록 내부에서 사용될 때 특별한 방식으로 처리되어야한다고 알려줍니다. 일반적으로 블록에도 사용되는 변수 및 해당 내용은 복사되므로 이러한 변수에 대한 수정 내용은 블록 외부에 표시되지 않습니다. 로 표시되면 __block블록 내부에서 수행 한 수정 내용도 외부에서 볼 수 있습니다.

예제와 추가 정보 는 Apple 블록 프로그래밍 주제__block 스토리지 유형 을 참조하십시오 .

중요한 예는 다음과 같습니다.

extern NSInteger CounterGlobal;
static NSInteger CounterStatic;

{
    NSInteger localCounter = 42;
    __block char localCharacter;

    void (^aBlock)(void) = ^(void) {
        ++CounterGlobal;
        ++CounterStatic;
        CounterGlobal = localCounter; // localCounter fixed at block creation
        localCharacter = 'a'; // sets localCharacter in enclosing scope
    };

    ++localCounter; // unseen by the block
    localCharacter = 'b';

    aBlock(); // execute the block
    // localCharacter now 'a'
}

이 예에서, 양 localCounterlocalCharacter블록이 호출되기 전에 수정된다. 그러나 블록 내부 localCharacter에서는 __block키워드 덕분에 수정 사항 만 표시 됩니다. 반대로, 블록은 수정할 수 있으며이 localCharacter수정은 블록 외부에서 볼 수 있습니다.


8
훌륭하고 간결한 설명과 매우 유용한 예입니다. 감사!
Evan Stone

1
aBlock은 localCounter를 어떻게 수정합니까? CounterGlobal 만 수정하는 것 같습니다. 감사합니다
CommaToast

8
수정하지는 않지만 수정 localCounter합니다 localCharacter. 또한 localCounter블록 에있는 값에주의를 기울이십시오 . 블록이 호출 되기 전에 변수가 커지지 만 블록이 작성된 (값이 "캡처 된"시점) 변수는 42 입니다.
DarkDust

1
도움이되는 설명이지만 설명에 모순되는 내용이 무엇인지 설명 할 수 있습니까? 위에서 "aBlock은 ... localCounter를 수정합니다"라고 말한 다음 주석에서 "[aBlock]은 localCounter를 수정하지 않습니다."라고 말합니다. 무엇 이니? "수정되지 않은"경우 답변을 편집해야합니까?
Praxiteles

2
일반적으로 __block이없는 var는 값으로 캡처되어 블록이 생성 될 때 블록의 "환경"에 압축됩니다. 그러나 __block vars는 블록 내부 또는 외부에서 사용될 때마다 캡처되지 않습니다.
jchnxu

27

@bbum은 블로그 게시물 에서 깊이있는 블록을 다루고 __block 스토리지 유형을 다룹니다 .

__block은 고유 한 저장소 유형입니다.

정적, 자동 및 휘발성과 마찬가지로 __block은 스토리지 유형입니다. 변수의 스토리지를 다르게 관리하도록 컴파일러에 지시합니다.

...

그러나 __block 변수의 경우 블록이 유지되지 않습니다. 필요에 따라 유지 및 해제는 사용자의 책임입니다.
...

유스 케이스의 경우 __block인수를 유지하지 않기 때문에 유지주기를 피하기 위해 때때로 사용됩니다. 일반적인 예는 self를 사용하는 것입니다.

//Now using myself inside a block will not 
//retain the value therefore breaking a
//possible retain cycle.
__block id myself = self;

다음 사이클의 문제를 유지에 대한 자세한 정보를 원하시면,이 게시물을 참조 benscheirman.com/2012/01/...을 . 것 __weak뿐만 아니라이 특정한 경우에 충분? 아마도 좀 더 명확 할 것입니다.
Hari Karam Singh

17
마지막으로 __block을 사용하여 강력한 참조주기 (일명 유지주기)를 피할 수 있다는 주장은 ARC 상황에서 명백히 잘못되었습니다. ARC __block에서 변수가 강력하게 참조되므로 실제로 변수를 유발할 가능성이 더 큽니다. stackoverflow.com/a/19228179/189006
Krishnan

10

일반적으로 __block을 사용하지 않으면 블록은 변수를 복사 (유지)하므로 변수를 수정하더라도 블록은 이전 객체에 액세스 할 수 있습니다.

NSString* str = @"hello";
void (^theBlock)() = ^void() {
    NSLog(@"%@", str);
};
str = @"how are you";
theBlock(); //prints @"hello"

이 두 가지 경우 __block이 필요합니다.

1. 블록 내부의 변수를 수정하고 외부에서 볼 수있게하려면 :

__block NSString* str = @"hello";
void (^theBlock)() = ^void() {
    str = @"how are you";
};
theBlock();
NSLog(@"%@", str); //prints "how are you"

2. 블록을 선언 한 후 변수를 수정하고 블록에서 변경 사항을 볼 것으로 예상하는 경우 :

__block NSString* str = @"hello";
void (^theBlock)() = ^void() {
    NSLog(@"%@", str);
};
str = @"how are you";
theBlock(); //prints "how are you"

8

__block 은 두 가지 방법으로 사용할 수있는 스토리지 한정자입니다.

  1. 변수가 원래 변수의 어휘 범위와 해당 범위 내에 선언 된 블록간에 공유되는 스토리지에 있음을 표시합니다. 그리고 clang은이 변수를 나타내는 구조체를 생성하고이 구조체를 값이 아닌 참조로 사용합니다.

  2. MRC에서 __block 을 사용하면 블록이 캡처하는 객체 변수를 유지하지 않아도 됩니다. 이것이 ARC에서 작동하지 않도록주의하십시오. ARC에서는 __weak을 대신 사용해야 합니다.

당신은 참조 할 수 있습니다 사과 문서 자세한 정보는.


6

__block스코프 변수를 변경 가능하게 만드는 데 사용되는 스토리지 유형입니다.이 지정자로 변수를 선언하면 참조가 읽기 전용 사본이 아닌 블록으로 전달됩니다. 자세한 내용은 iOS의 블록 프로그래밍을 참조하십시오


2

이것이 당신을 도울 수 있기를 바랍니다

다음과 같은 코드가 있다고 가정 해 봅시다.

{
     int stackVariable = 1;

     blockName = ^()
     {
      stackVariable++;
     }
}

블록 내의 스택 변수는 기본적으로 불변이므로 "변수를 지정할 수 없습니다"와 같은 오류가 발생합니다.

선언 앞에 __block (storage modifier)을 추가하면 블록 내부에서 변경할 수 있습니다. __block int stackVariable=1;


1

로부터 블록 언어 사양 :

새로운 블록 유형 외에도 로컬 변수에 대한 새로운 스토리지 한정자 __block이 도입되었습니다. [testme : 블록 리터럴 내 __block 선언] __block 저장소 한정자는 기존 로컬 저장소 한정자 auto, register 및 static과 상호 배타적입니다. [testme] __block으로 한정된 변수는 할당 된 저장소에있는 것처럼 작동하며이 저장소는 상기 변수를 마지막으로 사용한 후에 자동적으로 회복된다. 구현은 스토리지가 초기에 자동이며 참조 블록의 Block_copy에 따라 할당 된 (힙) 스토리지로만 "이동"하는 최적화를 선택할 수 있습니다. 이러한 변수는 정상 변수처럼 변이 될 수 있습니다.

__block 변수가 블록 인 경우 __block 변수가 할당 된 저장소에 상주한다고 가정해야하며, 따라서 할당 된 저장소에도있는 블록을 참조한다고 가정합니다 (Block_copy 작업의 결과 임). 그럼에도 불구하고 구현에서 블록에 대한 초기 자동 스토리지를 제공하는 경우 Block_copy 또는 Block_release를 수행 할 준비가 없습니다. 이는 공유 변수를 업데이트하려는 잠재적으로 여러 스레드의 고유 경쟁 조건 및 이전 값을 폐기하고 새 값을 복사하는 것과 동기화해야하기 때문입니다. 이러한 동기화는이 언어 사양의 범위를 벗어납니다.

__block 변수가 무엇을 컴파일해야하는지에 대한 자세한 내용은 2.3 블록 구현 사양 섹션을 참조하십시오 .


귀하의 링크는 모두 죽었습니다
Ben Leggiero

이것은 실제로 답이 아니며 다듬거나 제거 될 수 있습니다. 감사!
Dan Rosenstark

0

접두사 인 변수를 블록 내에서 사용할 수 있음을 의미합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.