15

Vì vậy, tôi đang sử dụng các khối đệ quy. Tôi hiểu rằng đối với một khối được đệ quy nó cần phải được bắt đầu bằng từ khóa __block, và nó phải được sao chép để nó có thể được đặt trên heap. Tuy nhiên, khi tôi làm điều này, nó được hiển thị như là một rò rỉ trong dụng cụ. Có ai biết tại sao hay làm thế nào tôi có thể vượt qua nó?Các khối đệ quy trong rò rỉ mục tiêu-C trong ARC

Vui lòng lưu ý trong mã bên dưới tôi đã tham chiếu đến nhiều khối khác, nhưng không có mục nào trong số đó là đệ quy.

__block NSDecimalNumber *(^ProcessElementStack)(LinkedList *, NSString *) = [^NSDecimalNumber *(LinkedList *cformula, NSString *function){ 
     LinkedList *list = [[LinkedList alloc] init]; 
     NSDictionary *dict; 
     FormulaType type; 
     while (cformula.count > 0) { 
      dict = cformula.pop; 
      type = [[dict objectForKey:@"type"] intValue]; 
      if (type == formulaOperandOpenParen || type == formulaListOperand || type == formulaOpenParen) [list add:ProcessElementStack(cformula, [dict objectForKey:@"name"])]; 
      else if (type == formulaField || type == formulaConstant) [list add:NumberForDict(dict)]; 
      else if (type == formulaOperand) [list add:[dict objectForKey:@"name"]]; 
      else if (type == formulaCloseParen) { 
       if (function){ 
        if ([function isEqualToString:@"AVG("]) return Average(list); 
        if ([function isEqualToString:@"MIN("]) return Minimum(list); 
        if ([function isEqualToString:@"MAX("]) return Maximum(list); 
        if ([function isEqualToString:@"SQRT("]) return SquareRoot(list); 
        if ([function isEqualToString:@"ABS("]) return EvaluateStack(list).absoluteValue; 
        return EvaluateStack(list); 
       } else break; 
      } 
     } 
     return EvaluateStack(list); 
    } copy]; 
    NSDecimalNumber *number = ProcessElementStack([formula copy], nil); 

CẬP NHẬT Vì vậy, trong nghiên cứu của riêng tôi, tôi đã phát hiện ra rằng vấn đề dường như không phải làm gì với các tham chiếu đến các khối khác khối này sử dụng. Nếu tôi làm điều gì đó đơn giản như thế này, nó không bị rò rỉ:

__block void (^LeakingBlock)(int) = [^(int i){ 
     i++; 
     if (i < 100) LeakingBlock(i); 
    } copy]; 
    LeakingBlock(1); 

Tuy nhiên, nếu tôi thêm một khối khác trong này, nó bị rò rỉ:

void (^Log)(int) = ^(int i){ 
    NSLog(@"log sub %i", i); 
}; 

__block void (^LeakingBlock)(int) = [^(int i){ 
    Log(i); 
    i++; 
    if (i < 100) LeakingBlock(i); 
} copy]; 
LeakingBlock(1); 

Tôi đã thử bằng cách sử dụng __block từ khóa cho Log() và cũng đã thử sao chép nó, nhưng nó vẫn bị rò rỉ. Bất kỳ ý tưởng?

CẬP NHẬT 2 Tôi đã tìm ra cách ngăn chặn sự rò rỉ, nhưng hơi khó chịu. Nếu tôi chuyển đổi thông qua trong khối để một id yếu, và sau đó đúc id yếu trở lại vào một loại khối, tôi có thể ngăn chặn rò rỉ.

void (^Log)(int) = ^(int i){ 
    NSLog(@"log sub %i", i); 
}; 

__weak id WeakLogID = Log; 

__block void (^LeakingBlock)(int) = [^(int i){ 
    void (^WeakLog)(int) = WeakLogID; 
    WeakLog(i); 
    if (i < 100) LeakingBlock(++i); 
} copy]; 
LeakingBlock(1); 

Chắc chắn có cách nào tốt hơn?

+0

Cảm ơn bạn đã chia sẻ nghiên cứu của mình, tôi chưa từng nghe nói về việc phải sao chép khối. Tuy nhiên, có vẻ như một LLVM gần đây phát ra một cảnh báo tại cuộc gọi đệ quy "nắm bắt LeakingBlock" mạnh mẽ trong khối này có khả năng dẫn đến một chu kỳ giữ lại ".Cách duy nhất tôi tìm thấy để xoa dịu trình biên dịch là sử dụng một ptr yếu riêng biệt cho khối hơi tương tự như câu trả lời của bạn dưới đây, mặc dù nó khó sử dụng mà tôi bị cám dỗ để ghi đè cục bộ cảnh báo thay thế. Tôi muốn được quan tâm đến việc xem xét của bạn một khi bạn thử trình biên dịch mới nhất. –

+0

@smallduck Ban đầu, tôi đã sử dụng 'copy' vì nó khiến khối được sao chép sang đống từ ngăn xếp. Trong một thời gian mà làm việc tốt và tôi cũng đã nhận lỗi trình biên dịch "đệ quy". Tôi đã xóa 'copy' khỏi mã của tôi (như được phản ánh trong câu trả lời của tôi) và nó đã hoạt động (trong khi trước đó tôi nhận được' EXC_BAD_ACCESS'. Tôi đoán rằng Apple đã thay đổi từ khóa '__block' để tạo các khối trên heap thay vì ngăn xếp ... nhưng đó chỉ là phỏng đoán –

+0

@smallduck Thật ra, tôi đã từ bỏ việc sử dụng các khối để đệ quy. Có, nó có thể được thực hiện nhưng hơi khó sử dụng và có quá nhiều cạm bẫy. giữ lại các chu kỳ (có thể rất tệ khi đệ quy) và nó trở nên khó đọc Vì vậy, thông thường tôi chỉ cần gắn các phương thức/chức năng để thực hiện đệ quy –

Trả lời

11

Ok, tôi tự tìm được câu trả lời ... nhưng nhờ những người đã cố giúp đỡ.

Nếu bạn đang tham khảo/sử dụng các khối khác trong khối đệ quy, bạn phải chuyển chúng thành các biến yếu. Tất nhiên, __weak chỉ áp dụng cho các kiểu con trỏ chặn, vì vậy bạn phải gõ chúng trước. Đây là giải pháp cuối cùng:

typedef void (^IntBlock)(int); 

    IntBlock __weak Log = ^(int i){ 
     NSLog(@"log sub %i", i); 
    }; 

    __block void (^LeakingBlock)(int) = ^(int i){ 
     Log(i); 
     if (i < 100) LeakingBlock(++i); 
    }; 
    LeakingBlock(1); 

Mã trên không bị rò rỉ.

+1

Bạn không cần typedef nếu bạn không muốn nó: 'void (^ __ log yếu) (int) =^(int i) {...};' –

+2

@MattWilding Tốt. Tôi nghĩ rằng đã không được sử dụng để làm việc trong các phiên bản trước của Xcode. Trình biên dịch dường như liên tục thay đổi liên quan đến Blocks. Tôi vừa tải xuống Xcode 4.6 và bây giờ nó phàn nàn rằng mã trên "có thể" dẫn đến một chu kỳ giữ lại. Tôi nghĩ Apple vẫn đang tìm ra toàn bộ "Block". –

+1

Tôi dường như thay đổi mã khối của mình bằng mỗi lần sửa đổi trình biên dịch. Tôi đã có cảnh báo tương tự với 4.6, và bạn có thể sửa nó bằng cách đánh dấu 'LeakingBlock' với modifier' __weak'. –

-1

Không có thông tin bối cảnh hơn nữa, tôi có thể nói điều này:

Bạn đang bị rò rỉ khối đó bởi vì bạn đang sao chép nó và không phát hành nó ở nơi khác. Bạn cần phải sao chép nó để di chuyển nó vào heap, đó là ok. Nhưng cách bạn chọn không hoàn toàn ổn.

Cách chính xác để thực hiện việc này là lưu trữ nó dưới dạng một biến đối tượng, sao chép và sau đó giải phóng nó bên trong dealloc. Ít nhất, đó là một cách để làm điều đó mà không bị rò rỉ.

+2

Tôi không thể giải phóng nó theo cách thủ công. tôi làm điều đó, phạm vi của khối là phương pháp, do đó, sử dụng một iVar là mùi mã. Khối này sẽ được phát hành ở cuối phương thức –

+0

Ông đang sử dụng ARC – Eonil

0

Aaron,

Vì mã của bạn có vẻ là một luồng, tại sao bạn sao chép khối? Nếu bạn không sao chép khối, bạn không bị rò rỉ.

Andrew

+0

Có đúng, mã này là duy nhất Tôi không chắc chắn tôi hiểu rõ điểm tuyên bố đó. Nếu tôi không sao chép khối, tôi nhận được EXC_BAC_ACCESS khi khối cố gắng truy cập chính nó một cách đệ quy. –

+1

Thực ra, tôi nên làm rõ: Nếu không sao chép khối, tôi nhận được EXC_BAD_ACCESS khi gán, không phải khi khối cố gắng truy cập đệ quy một cách đệ quy (điều đó xảy ra nếu tôi không sử dụng __block). Tôi là một chút không chắc chắn về các chi tiết, nhưng tôi tin rằng đó là vì khối ban đầu được tạo ra như một đối tượng const trên stack, trong khi khối được tham chiếu bên trong chính nó là khối ngăn xếp const rất giống nhau. Điều này được cố định bằng cách sao chép khối vào heap trước, sau đó gán nó cho biến __block var để nó có thể tham chiếu một bản sao của chính nó chứ không phải là chính nó. –

Các vấn đề liên quan