[ios] ARC에서 항상 자신에 대한 약한 참조를 블록으로 전달합니까?

Objective-C의 블록 사용에 대해 약간 혼란 스럽습니다. 현재 ARC를 사용하고 있으며 앱에 많은 블록이 있으며 현재 self약한 참조 대신 항상 참조합니다 . 이러한 블록이 유지 self되고 할당이 해제되지 않도록 하는 원인이 될 수 있습니까? 문제는 항상 블록에서 weak참조를 사용해야 self합니까?

-(void)handleNewerData:(NSArray *)arr
{
    ProcessOperation *operation =
    [[ProcessOperation alloc] initWithDataToProcess:arr
                                         completion:^(NSMutableArray *rows) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self updateFeed:arr rows:rows];
        });
    }];
    [dataProcessQueue addOperation:operation];
}

ProcessOperation.h

@interface ProcessOperation : NSOperation
{
    NSMutableArray *dataArr;
    NSMutableArray *rowHeightsArr;
    void (^callback)(NSMutableArray *rows);
}

ProcessOperation.m

-(id)initWithDataToProcess:(NSArray *)data completion:(void (^)(NSMutableArray *rows))cb{

    if(self =[super init]){
        dataArr = [NSMutableArray arrayWithArray:data];
        rowHeightsArr = [NSMutableArray new];
        callback = cb;
    }
    return self;
}

- (void)main {
    @autoreleasepool {
        ...
        callback(rowHeightsArr);
    }
}



답변

토론의 일부 strong또는 weak일부에 집중하지 않는 것이 좋습니다. 대신 사이클 부분 에 집중하십시오 .

A는 유지 주기가 객체 A는 객체 B를 유지, 때 일어나는 루프 두 객체가 해제되는 경우 개체 B가, 그 상황에서 개체 A를 유지한다 :

  • 오브젝트 B는 이에 대한 참조를 보유하므로 오브젝트 A는 할당 해제되지 않습니다.
  • 그러나 오브젝트 A가 참조하는 한 오브젝트 B는 할당 해제되지 않습니다.
  • 그러나 오브젝트 B는 참조를 보유하므로 오브젝트 A는 할당 해제되지 않습니다.
  • 광고 인피니티 움

따라서,이 두 객체는 ​​모든 것이 제대로 작동한다면 할당이 해제 되더라도 프로그램 수명 동안 메모리에 매달리게됩니다.

우리가 걱정하는 것은 유지하는 것입니다 주기를 ,이주기를 생성 및 자신의 블록에 대해 아무것도가 없습니다. 이것은 문제가되지 않습니다. 예를 들면 :

[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
   [self doSomethingWithObject:obj];
}];

블록은 유지 self하지만 블록은 유지 self하지 않습니다. 둘 중 하나가 해제되면주기가 생성되지 않고 모든 것이 할당 해제됩니다.

문제가 생기는 곳은 다음과 같습니다.

//In the interface:
@property (strong) void(^myBlock)(id obj, NSUInteger idx, BOOL *stop);

//In the implementation:
[self setMyBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  [self doSomethingWithObj:obj];
}];

이제 객체 ( self) strong에 블록에 대한 명시적인 참조가 있습니다. 그리고 블록은 암시 적 강한 참조가 self있습니다. 그것은주기이며 이제는 어느 개체도 올바르게 할당 해제되지 않습니다.

이와 같은 상황에서 self 정의에 따라 이미 strong블록에 대한 참조가 있으므로 일반적으로 self블록에서 사용할 약한 참조를 만들어서 해결하는 것이 가장 쉽습니다 .

__weak MyObject *weakSelf = self;
[self setMyBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  [weakSelf doSomethingWithObj:obj];
}];

그러나 이것은 호출하는 블록을 다룰 때 따르는 기본 패턴이 아니어야합니다self ! 이것은 자기 자신과 블록 사이의 유지주기를 중단시키는 데만 사용해야합니다. 이 패턴을 어디에서나 채택해야한다면 self할당이 해제 된 후에 블록을 전달할 위험이 있습니다.

//SUSPICIOUS EXAMPLE:
__weak MyObject *weakSelf = self;
[[SomeOtherObject alloc] initWithCompletion:^{
  //By the time this gets called, "weakSelf" might be nil because it's not retained!
  [weakSelf doSomething];
}];


답변

항상 약한 참조를 사용할 필요는 없습니다. 블록이 유지되지 않고 실행 된 후 삭제되면 유지주기를 만들지 않으므로 강력하게 자체를 캡처 할 수 있습니다. 어떤 경우에는 블록이 완료 될 때까지 블록이 자체를 보유하여 블록이 조기에 할당 해제되지 않도록 할 수도 있습니다. 그러나 블록을 강력하게 캡처하고 캡처 자체 내에 있으면 유지주기가 생성됩니다.


답변

나는 @jemmons에 전적으로 동의합니다.

그러나 이것은 스스로 호출하는 블록을 다룰 때 따르는 기본 패턴이 아니어야합니다! 이것은 자기 자신과 블록 사이의 유지주기를 중단시키는 데만 사용해야합니다. 이 패턴을 어디에서나 채택해야한다면, 자기 할당이 해제 된 후에 실행 된 것으로 블록을 전달할 위험이 있습니다.

//SUSPICIOUS EXAMPLE:
__weak MyObject *weakSelf = self;
[[SomeOtherObject alloc] initWithCompletion:^{
  //By the time this gets called, "weakSelf" might be nil because it's not  retained!
  [weakSelf doSomething];
}];

이 문제를 극복하기 위해 weakSelf블록 내부에 대한 강력한 참조를 정의 할 수 있습니다 .

__weak MyObject *weakSelf = self;
[[SomeOtherObject alloc] initWithCompletion:^{
  MyObject *strongSelf = weakSelf;
  [strongSelf doSomething];
}];


답변

Leo가 지적한 것처럼 질문에 추가 한 코드는 강력한 참조주기 (일명 유지주기)를 나타내지 않습니다. 강력한 참조주기를 유발할 수있는 작업 관련 문제 중 하나는 작업이 릴리스되지 않은 경우입니다. 코드 스 니펫은 작업을 동시에 수행하도록 정의하지 않았지만 제안한 경우 게시하지 않았 isFinished거나 순환 종속성이있는 경우 해제되지 않습니다 . 그리고 작업이 해제되지 않으면 뷰 컨트롤러도 해제되지 않습니다. 중단 점을 추가하거나 NSLog작업을 수행 하는 것이 좋습니다.dealloc 방법 호출되는지 확인하는 .

당신은 말했다 :

유지주기의 개념을 이해하지만 블록에서 어떤 일이 발생하는지 잘 모르겠으므로 조금 혼란 스럽습니다.

블록에서 발생하는 유지주기 (강한 참조주기) 문제는 익숙한 유지주기 문제와 같습니다. 블록은 블록 내에 나타나는 객체에 대한 강력한 참조를 유지하며 블록 자체가 해제 될 때까지 이러한 강력한 참조를 해제하지 않습니다. 따라서 블록 참조 self또는 인스턴스 변수를 참조하는 경우 self자체에 대한 강력한 참조를 유지하면 블록이 해제 될 때까지 (또는이 경우에는NSOperation 서브 클래스가 해제 .

자세한 내용 은 Objective-C를 사용한 프로그래밍 : 블록 작업자체 캡처시 강력한 참조주기 방지 섹션을 참조하십시오 . 문서의 .

뷰 컨트롤러가 여전히 릴리스되지 않으면 해결되지 않은 강력한 참조가있는 위치를 식별하면됩니다 ( NSOperation할당이 해제 되었다고 가정 한 경우 ). 일반적인 예는 반복을 사용하는 것입니다 NSTimer. 또는 delegate잘못 strong참조를 유지 관리하는 일부 사용자 지정 또는 기타 개체입니다 . 다음과 같이 계측기를 사용하여 객체가 강한 참조를 얻는 위치를 추적 할 수 있습니다.

Xcode 6의 레코드 참조 카운트

또는 Xcode 5에서 :

Xcode 5의 레코드 참조 카운트


답변

일부 설명은 유지주기에 대한 조건을 무시합니다. [객체 그룹이 강력한 관계의 원으로 연결되어 있으면 그룹 외부에서 강한 참조가없는 경우에도 서로를 유지합니다.] 자세한 내용은 문서를 읽으십시오 .


답변

다음은 블록 내부에서 자기를 사용하는 방법입니다.

// 블록 호출

 NSString *returnedText= checkIfOutsideMethodIsCalled(self);

NSString* (^checkIfOutsideMethodIsCalled)(*)=^NSString*(id obj)
{
             [obj MethodNameYouWantToCall]; // this is how it will call the object 
            return @"Called";


};


답변