5

Tôi có một số mã khối đệ quy trong mục tiêu-c đang gây ra lỗi EXC_BAD_ACCESS.Khối đệ quy mục tiêu-c với chủ đề EXC_BAD_ACCESS

- (void) doSomethingWithCompletion:(void (^)())completion { 
    if (completion) { 
     dispatch_async(dispatch_get_main_queue(), completion); 
    } 
} 

- (void) testBlocks { 

    NSString *testString = @"hello"; 

    __block NSInteger count = 0; 

    __block __weak void (^weak_block)(NSString *); 
    void(^strong_block)(NSString *); 
    weak_block = strong_block = ^(NSString *str) { 

     [self doSomethingWithCompletion:^{ 
      NSLog(@"number: %zd", count); 
      if (++count < 10) { 
       weak_block(str); 
      } 
     }]; 


    }; 
    strong_block(testString); 
} 

Lỗi xảy ra trên weak_block (str) mà tôi giả định là vì nó được phát hành khi dispatch_async được gọi. Gọi strong_block (str) trong đó diễn ra khi nó được khai báo bằng __block như vậy:

__block void(^strong_block)(NSString *); 

Nguyên nhân một cảnh báo 'chụp 'strong_block' mạnh mẽ trong khối này là khả năng dẫn đến một chu kỳ giữ'.

Vì vậy, tôi đã thay đổi phương pháp testBlock không sử dụng một tham chiếu yếu như sau:

- (void) testBlocks { 

    NSString *testString = @"hello"; 

    __block NSInteger count = 0; 

    __block void (^inner_block)(NSString *); 
    void(^strong_block)(NSString *); 
    inner_block = strong_block = ^(NSString *str) { 

     [self doSomethingWithCompletion:^{ 
      NSLog(@"number: %zd", count); 
      if (++count < 10) { 
       inner_block(str); 
      } 
     }]; 


    }; 
    strong_block(testString); 
} 

Nhưng tôi không chắc chắn nếu điều này gây ra một giữ lại chu kỳ hoặc nếu thêm

__block void (^inner_block)(NSString *) = weak_block; 

bên trong khối thay vì sẽ gây ra một chu kỳ giữ lại là tốt. Cách chính xác để xử lý tình huống này là gì?

+1

não của anh đau nhìn này .. –

Trả lời

1

Nó treo do khối (được chỉ định bởi weak_block, strong_block) đã được deallocated bởi thời gian khối "hoàn thành" chạy, và gọi một khối với một con trỏ khối nil bị treo.

Khối được deallocated vì không có tài liệu tham khảo mạnh mẽ để nó sau khi testBlocks trả về.

Chế độ thứ hai sẽ có chu kỳ giữ lại vì khối chụp được inner_block, có chứa tham chiếu mạnh mẽ cho chính nó.

Cách đúng là để tạo ra một tài liệu tham khảo mạnh mẽ từ các tài liệu tham khảo yếu bị bắt bên trong khối, và để cho việc bắt giữ khối hoàn rằng:

- (void) testBlocks { 

    NSString *testString = @"hello"; 

    __block NSInteger count = 0; 

    __block __weak void (^weak_block)(NSString *); 
    void(^strong_block)(NSString *); 
    weak_block = strong_block = ^(NSString *str) { 

     void(^inner_block)(NSString *) = weak_block; 
     [self doSomethingWithCompletion:^{ 
      NSLog(@"number: %zd", count); 
      if (++count < 10) { 
       inner_block(str); 
      } 
     }]; 


    }; 
    strong_block(testString); 
} 
+0

Điều này làm việc cho tôi, lỗi của tôi là tôi đã sử dụng: typeof (weak_block) inner_block = weak_block; thay vì void (^ inner_block) (NSString *) = weak_block; khiến nó yếu đi thay vì mạnh. – malhal

0

Không chắc đây là bằng chứng về một trong hai khả năng, nhưng nếu bạn thêm thuộc tính khối weak và kiểm tra một rằng sau tất cả những thứ block đã chạy ...

... 
@property (weak) void (^true_weak_block)(NSString *); 
@property (weak) NSString *weak_string; 
... 


- (void) testBlocks { 

NSString *strong_string = [NSString stringWithFormat:@"%@", @"some string"]; // note that you can not use a string literal in this example.. 
self.weak_string = strong_string;  

NSString *testString = @"hello"; 

__block NSInteger count = 0; 

__block void (^inner_block)(NSString *); 
void(^strong_block)(NSString *); 
inner_block = strong_block = ^(NSString *str) { 

    [self doSomethingWithCompletion:^{ 
     NSLog(@"number: %zd", count); 
     if (++count < 10) { 
      inner_block(str); 
     } 
    }]; 


}; 
self.true_week_block = strong_block; 
[self test]; 
strong_block(testString); 
} 

- (void)test { 
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 
     NSLog(@"%@", self.true_week_block); // not deallocated 
     NSLog(@"%@", self.weak_string); // deallocated 
    }); 
} 

Trong thử nghiệm của tôi khối không bao giờ được deallocated và cũng địa chỉ bộ nhớ vẫn giữ nguyên theo thời gian, ngay cả khi bạn thay đổi việc gán đều đặn của hai khối mạnh để sử dụng bản sao thay vì chỉ định giữ lại ngầm.

+0

Hey Tôi nghĩ là một thuộc tính của lớp true_weak_block sẽ không được phát hành cho đến khi lớp đó được phát hành đúng không? hay tôi chỉ bối rối ở đây? – richy

+0

Nếu nó hoạt động giống như một đối tượng thông thường, nó sẽ hoạt động như mong đợi ở đây vì gán cho yếu sẽ không làm nhiệm vụ refcount vì vậy khi tham chiếu ban đầu đi ra khỏi phạm vi nó sẽ được deallocated. –

+0

Được cập nhật với thử nghiệm s với đối tượng thông thường. –