이 블록에서 자기를 강력하게 캡처하면 유지주기가 발생할 수 있습니다


207

xcode 에서이 경고를 피하려면 어떻게해야합니까? 다음은 코드 스 니펫입니다.

[player(AVPlayer object) addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
queue:nil usingBlock:^(CMTime time) {
    current+=1;

    if(current==60)
    {
        min+=(current/60);
        current = 0;
    }

    [timerDisp(UILabel) setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];///warning occurs in this line
}];

timerDisp클래스의 속성은?
Tim

예, @property (nonatomic, strong) UILabel * timerDisp;
user1845209

2
어떤이는 다음과 같습니다 player(AVPlayer object)timerDisp(UILabel)?
Carl Veazey

AVPlayer * 플레이어; UILabel * timerDisp;
user1845209

5
실제 질문은 순환 참조가 손상 될 것임을 알 때 (예 : 네트워크 요청이 완료 될 때 항상 참조를 지우는 경우) 불필요한 자체 참조 없이이 경고를 끄는 방법 입니다.
Glenn Maynard

답변:


514

self여기에 대한 캡처는 내재적 속성 액세스와 함께 제공됩니다. self.timerDisp참조 할 수 없습니다selfself 됩니다. 블록에서 강력하게 유지 될 블록에서 속성을 하거나self .

블록 내부에 self액세스 하기 전에 약한 참조를 만들어이 문제를 해결할 수 있습니다 timerDisp.

__weak typeof(self) weakSelf = self;
[player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
                                     queue:nil
                                usingBlock:^(CMTime time) {
                                                current+=1;

                                                if(current==60)
                                                {
                                                    min+=(current/60);
                                                    current = 0;
                                                }

                                                 [weakSelf.timerDisp setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];
                                            }];

13
__unsafe_unretained대신 사용해보십시오 .
Tim

63
해결되었습니다. 대신 이것을 사용하십시오 : __unsafe_unretained typeof (self) weakSelf = self; 도움을 주셔서 감사합니다
@Tim

1
좋은 대답이지만, "자기 자신에 의해 강력하게 유지 될 블록 내에서 자기 자신이나 재산을 언급 할 수는 없습니다." 이것은 사실이 아닙니다. 아래 답변을 참조하십시오. 더 나은 "는 말을 한다 ... 당신이 자기를 참조하는 경우 상당한주의를 기울입니다"
크리스 수터

8
OP 코드에 유지주기가 표시되지 않습니다. 블록은에 의해 강력하게 유지되지 않고 self기본 디스패치 큐에 의해 유지됩니다. 내가 잘못?
erikprice

3
@erikprice : 당신은 틀리지 않습니다. 문제는 유지주기의 실제 존재에 대한 것이 아니라 Xcode가 제시하는 오류 ( "xcode에서이 경고를 피하는 방법")에 대한 질문으로 해석되었습니다. 제공된 스 니펫 OP에서만 유지주기가 분명하지 않다고 말하는 것이 맞습니다.
Tim

52
__weak MyClass *self_ = self; // that's enough
self.loadingDidFinishHandler = ^(NSArray *receivedItems, NSError *error){
    if (!error) {
       [self_ showAlertWithError:error];
    } else {
       self_.items = [NSArray arrayWithArray:receivedItems];
       [self_.tableView reloadData];
    }
};

그리고 기억해야 할 한 가지 중요한 점은 인스턴스 변수를 블록에서 직접 사용하지 말고 약한 객체의 속성으로 사용하십시오.

self.loadingDidFinishHandler = ^(NSArray *receivedItems, NSError *error){
        if (!error) {
           [self_ showAlertWithError:error];
        } else {
           self_.items = [NSArray arrayWithArray:receivedItems];
           [_tableView reloadData]; // BAD! IT ALSO WILL BRING YOU TO RETAIN LOOP
        }
 };

잊지 말고 :

- (void)dealloc {
    self.loadingCompletionHandler = NULL;
}

다른 사람이 보유하지 않은 약한 사본을 전달하면 다른 문제가 발생할 수 있습니다.

MyViewController *vcToGo = [[MyViewCOntroller alloc] init];
__weak MyViewController *vcToGo_ = vcToGo;
self.loadingCompletion = ^{
    [vcToGo_ doSomePrecessing];
};

경우 vcToGo해제 한 후이 블록 해고 될 것입니다 당신이 포함되어있는 휴지통으로 인식 할 수없는 선택기 충돌을 얻을 것으로 예상 vcToGo_이제 변수를. 그것을 제어하십시오.


3
또한 설명하면 더 강력한 답변이 될 것입니다.
Eric J.

43

더 나은 버전

__strong typeof(self) strongSelf = weakSelf;

블록의 첫 번째 줄로 약한 버전에 대한 강력한 참조를 작성하십시오. 블록이 실행을 시작하고 nil로 다시 떨어지지 않았을 때 self가 여전히 존재한다면,이 행은 블록의 실행 수명 동안 계속 유지되도록합니다.

따라서 모든 것이 다음과 같습니다.

// Establish the weak self reference
__weak typeof(self) weakSelf = self;

[player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
                                 queue:nil
                            usingBlock:^(CMTime time) {

    // Establish the strong self reference
    __strong typeof(self) strongSelf = weakSelf;

    if (strongSelf) {
        [strongSelf.timerDisp setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];
    } else {
        // self doesn't exist
    }
}];

이 기사를 여러 번 읽었습니다. Erica Sadun의 블록 및 NSNotificationCenter 사용시 문제를 피하는 방법 에 대한 훌륭한 기사입니다.


신속한 업데이트 :

예를 들어, 신속하게 성공 블록을 사용하는 간단한 방법은 다음과 같습니다.

func doSomeThingWithSuccessBlock(success: () -> ()) {
    success()
}

이 메소드를 호출 self하고 성공 블록에서 사용해야 할 때 . [weak self]guard let기능을 사용하겠습니다 .

    doSomeThingWithSuccessBlock { [weak self] () -> () in
        guard let strongSelf = self else { return }
        strongSelf.gridCollectionView.reloadData()
    }

이 소위 강약 댄스는 인기있는 오픈 소스 프로젝트에서 사용됩니다. Alamofire .

자세한 내용은 신속한 스타일 가이드를 확인하십시오.


어떻게 당신이 한 경우 typeof(self) strongSelf = self;(대신 __weak의) 블록의 외부를 다음 블록 말했다 strongSelf = nil;사용 후? 나는 당신의 예제가 블록이 실행될 때까지 weakSelf가 0이 아님을 어떻게 보장하는지 알지 못한다.
Matt

가능한 유지주기를 피하기 위해 코드에서 self를 사용하는 블록 외부에 약한 자체 참조를 설정합니다. 어떤 식 으로든, 블록이 실행되도록해야합니다. ur 코드의 또 다른 블록은 이제 이전에 보유한 메모리를 확보하는 역할을합니다.
Warif Akhand Rishi

@Matt이 예제의 목적은 weakSelf를 유지하지 않는 것입니다. 목적은 weakSelf가 0이 아닌 경우 블록 내부를 강력하게 참조하는 것입니다. 따라서 블록이 self로 실행을 시작하면 블록 내부에서 self가 없어지지 않습니다.
Warif Akhand Rishi

15

다른 대답으로, Tim은 다음과 같이 말했습니다.

자체적으로 강력하게 유지되는 블록 내에서 자체 또는 자체 속성을 참조 할 수 없습니다.

이것은 사실이 아닙니다. 어떤 시점에서 사이클을 중단하지 않는 한이 작업을 수행해도됩니다. 예를 들어, 자체를 유지하는 블록이있는 타이머가 작동하고 자체적으로 타이머에 대한 강력한 참조를 유지한다고 가정 해 봅시다. 어떤 시점에서 타이머를 파괴하고 사이클을 중단한다는 것을 항상 알고 있다면 이것은 완벽하게 좋습니다.

제 경우에는 다음과 같은 코드에 대한 경고 메시지가 나타납니다.

[x setY:^{ [x doSomething]; }];

이제 clang이 메소드가 "set"(및 여기서 언급하지 않은 다른 특별한 경우)으로 시작하는 것을 감지하면이 경고 만 생성한다는 것을 알게되었습니다. 나에게, 유지 루프가있을 위험이 없다는 것을 알고 있으므로 메서드 이름을 "useY"로 변경했습니다. 물론 모든 경우에 적합하지 않을 수 있으며 일반적으로 약한 참조를 사용하려고합니다. 다른 사람들을 도울 수 있도록 내 솔루션을 주목할 가치가 있다고 생각했습니다.


4

여러 번, 이것은 실제로 유지주기가 아닙니다 .

그렇지 않다는 것을 알면 과일이없는 약한 자아를 세상에 가져올 필요가 없습니다.

Apple은 API를 사용하여 이러한 경고를 강제로 수행합니다 UIPageViewController. 여기에는 set 메소드 (다른 곳에서 언급했듯이 경고를 트리거하여 블록 인 ivar에 값을 설정하는 것을 생각 함) 와 완료 처리기 블록 ( 의심 할 여지없이 자신을 참조하십시오).

한 줄의 코드에서 경고를 제거하는 컴파일러 지시문은 다음과 같습니다.

#pragma GCC diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
    [self.pageViewController setViewControllers:@[newViewController] direction:navigationDirection animated:YES completion:^(BOOL finished) {
        // this warning is caused because "setViewControllers" starts with "set…", it's not a problem
        [self doTheThingsIGottaDo:finished touchThePuppetHead:YES];
    }];
#pragma GCC diagnostic pop

1

정밀도와 스타일 향상에 2 센트 추가. 대부분의 self경우이 블록에서 하나 또는 두 개의 멤버 만 사용 하며 슬라이더를 업데이트하기 만합니다. 캐스팅 self이 과잉입니다. 대신 명시 적으로 작성하고 블록 내부에 실제로 필요한 객체 캐스트 하는 것이 좋습니다. 그것의 인스턴스 인 경우에 예를 들어 UISlider*말하자면, _timeSlider그냥 블록 선언하기 전에 다음을 수행하십시오

UISlider* __weak slider = _timeSlider;

그런 다음 slider블록 내부에서 사용 하십시오. 기술적으로 이것은 잠재적 유지주기를 내부의 모든 개체가 아니라 필요한 개체로만 좁히기 때문에 더 정확합니다 self.

전체 예 :

UISlider* __weak slider = _timeSlider;
[_embeddedPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 1)
     queue:nil
     usingBlock:^(CMTime time){
        slider.value = time.value/time.timescale;
     }
];

또한, 약한 포인터로 캐스팅되는 객체는 이미 내부에 약한 포인터 self일뿐 아니라 유지주기의 가능성을 최소화하거나 완전히 제거합니다. 위의 예에서 _timeSlider실제로는 약한 참조로 저장된 속성입니다. 예 :

@property (nonatomic, weak) IBOutlet UISlider* timeSlider;

C 및 C ++에서와 같이 코딩 스타일 측면에서 변수 선언을 오른쪽에서 왼쪽으로 읽는 것이 좋습니다. SomeType* __weak variable이 순서대로 선언 하면 오른쪽에서 왼쪽으로보다 자연스럽게 읽습니다 variable is a weak pointer to SomeType.


1

나는 최근에이 경고에 부딪 쳤고 그것을 조금 더 이해하고 싶었다. 약간의 시행 착오 끝에, 메소드가 "add"또는 "save"로 시작하는 것에서 비롯된 것을 발견했습니다. Objective C는 "new", "alloc"등으로 시작하는 메소드 이름을 보유 된 오브젝트를 리턴하는 것으로 취급하지만 "add"또는 "save"에 대해서는 언급하지 않습니다. 그러나이 방법으로 메소드 이름을 사용하면 :

[self addItemWithCompletionBlock:^(NSError *error) {
            [self done]; }];

[self done] 행에 경고가 표시됩니다. 그러나 이것은 :

[self itemWithCompletionBlock:^(NSError *error) {
    [self done]; }];

계속해서 "__weak __typeof (self) weakSelf = self"방식을 사용하여 내 객체를 참조하지만 실제로는 미래의 나 및 / 또는 다른 개발자를 혼동시키기 때문에 그렇게하는 것을 좋아하지 않습니다. 물론 "add"(또는 "save")도 사용할 수 없지만 방법의 의미를 없애기 때문에 더 나쁩니다.

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