2013-01-10 13 views
6

Lời nói đầu; Đây không phải là một câu hỏi chung "Tôi có một ứng dụng khổng lồ với một sự rò rỉ". Đó là một vấn đề cụ thể về tính năng đếm tham chiếu tự động không hoạt động bình thường trong một ứng dụng demo gần như tầm thường, với mã nguồn đầy đủ, hoặc tạo mã nhị phân hoặc vấn đề trình biên dịch hoặc lỗi trong công cụ. (TLDR: Ồ. Trên thực tế là một điều kiện chủng tộc nhỏ lẻ)Một bộ nhớ Phantom bị rò rỉ với ARC trong XCode 4.5 nơi dealloc chắc chắn được gọi hoặc một vấn đề cụ?

Tôi bối rối bởi thực tế là danh sách "Phân bổ" của cụ đang hiển thị rò rỉ thể hiện, tôi có một thể hiện của lớp đó, chỉ một, và ARC đang gây ra phương thức dealloc được gọi và tôi biết nó đang được gọi vì có một thông điệp NSLog được in khi dealloc được thực hiện, nhưng nó vẫn xuất hiện trong danh sách rò rỉ trong Instruments.

KeepCount không bao giờ vượt quá 1. Nó không được giữ lại bởi bất cứ ai, và nó đang được dealloc'd, nhưng nó có vẻ như nó là một "rò rỉ" bởi vì nó hiển thị như một trường hợp hoạt động trong rò rỉ của Instrument.

Làm cách nào có thể?

Tôi vẫn đang học Objective-C với ARC, và vì vậy tôi nghĩ rằng tôi phải mắc một lỗi mới bắt đầu phổ biến. Đây là duy nhất của tôi init và dealloc đối tượng của tôi:

- (id) initWithMessage:(NSString*)messageForUser 
{ 
    self = [super init]; 
    if (self) 
    { 
     _message = messageForUser; 
     NSLog(@"from constructor: %@",_message); 
    } 
    return self; 

} 

- (void)dealloc { 
    NSLog(@"Goodbye cruel world. One WPMyObject signing off."); 
    // [message release]; // ARC forbiddeth thee! Begone release. 
    _message = nil; 

    // [super dealloc]; // ARC forbiddeth explicit super dealloc 
} 

Chỉ cần để xem nếu tôi có thể, tôi cố gắng gọi [super dealloc] trong phương thức dealloc, và ARC khối bạn với một lỗi, đó là rất tốt vì nó sẽ làm điều đó cho bạn. Tuy nhiên khi tôi viết phương pháp init của riêng tôi, nó không chặn tôi.

Khi tôi chạy chương trình bằng cách sử dụng "Chạy" trong XCode, tôi nhận được thông điệp NSLog "tạm biệt" độc ác, như tôi đã hi vọng, khi dealloc được chạy, nhưng tôi cũng nhận được bằng chứng cho thấy trường hợp vẫn tồn tại khi kết thúc chạy, khi phân tích dụng cụ sử dụng mẫu rò rỉ bộ nhớ được sử dụng:

Nếu tôi chạy mà không có mã tạo cá thể dưới đây, tôi chỉ nhận được một vài lỗ rò thư viện chuẩn, nhưng nếu tôi thêm mã này, rò rỉ xảy ra:

WPMyObject * myObject = [[WPMyObject alloc] initWithMessage: @"Hello World!\n" ]; 

Đây là những gì tôi thấy và nghĩ rằng tôi hiểu đang nói với tôi rằng trường hợp duy nhất của WPMyObject bị rò rỉ:

enter image description here

Mã nguồn đầy đủ mà là khá tầm thường (Mac OS X dòng lệnh ứng dụng nhỏ gọn sử dụng Objective-C và Foundation/Foundation.h) là trên BitBucket. Nhấp vào this link để xem mã nguồn trong trình duyệt của bạn.

Đơn vị main.m chạy một đối tượng thử nghiệm tạo ra trường hợp duy nhất, bên trong một tuyên bố @autoreleasepool {...} bối cảnh:

enter image description here

Nếu bạn muốn xem mã hoàn toàn tầm thường trên máy tính riêng của bạn, lấy nó như thế này :

hg clone https://bitbucket.org/wpostma/objectivecplaymac 

cập nhật: Bạn có thể sửa chữa "rò rỉ" (có thể là một lỗi trong Instruments, hoặc kêu vang/llvm biên dịch lỗi, và không phải là một "rò rỉ thực") bằng cách thêm dòng mã này ngay trước khi số } kết thúc hồ bơi tự động trả lời:

NSLog(@"Reached end of autorelease pool"); 

Đúng. Thêm thông điệp tường trình. "Rò rỉ" biến mất.Đây là XCode 4.5.2 (4G2008a) trên Mac OS X 10.7.5, chứa các công cụ Phiên bản 4.5 (xây dựng 4523).

Update2: Nó thực sự là một điều kiện đua đa tiến trình đơn giản. Đoạn mã dưới đây có vẻ là một lỗi hack-xây dựng một cách hợp lý. Nếu có một cách rõ ràng hơn để nói "chờ cho đến khi Nhạc được thực hiện với tôi, và sau đó thoát khỏi main()", đó có thể là một tính năng tương lai tốt đẹp, các kỹ sư của Apple. Một PROFILER_SYNC ("MESSAGE") macro mở rộng thành không có gì trong chế độ phát hành, nhưng trong quá trình xây dựng gỡ lỗi, gửi "MESSAGE" tới profiler .... Nó có thể rất tiện dụng.

int main(int argc, const char * argv[]) 
{ 
    // This creates and cleans up an auto-release pool context. 
    @autoreleasepool { 

     foo(); // Microscopic amounts of debug code you want profiler to analyze go here. 

#ifdef DEBUG 
     usleep(10000); // race condition prevention in debug builds, so Instruments can finish up. 
#endif 

    } 
#ifdef DEBUG 
    usleep(10000); // race condition prevention in debug builds, so Instruments can finish up. 
#endif 

    return 0; 
} 

Trả lời

3

Âm thanh giống như rò rỉ và giống như điều kiện đua. Một cuộc đua giữa các văn bản của NSLog và thực thi chương trình được chấm dứt. Hoặc một vấn đề đệm, nhiều khả năng, nơi đầu ra không được flushed cho đến khi đạt đến một ngưỡng nhất định.

Thử đặt sleep(100); sau cú đúp đóng trên hồ bơi tự động. Hoặc thử thêm một loạt văn bản vào thông điệp tường trình trong dealloc.

Nếu điều đó không "khắc phục", hãy hiển thị việc tháo gỡ và xem những thay đổi giữa hai phiên bản của mã.


Lý do tại sao điều này xảy ra: Cả hai công cụ và NSLog() được đệm hiệu quả vì lý do hiệu suất. Chúng được thiết kế để sử dụng trong một quy trình chạy tương đối dài, hầu như luôn luôn có một vòng lặp sự kiện chính hoặc một cuộc gọi đến dispatch_main().

Điều này được thực hiện để thử và tối thiểu tác động đến hiệu suất của ứng dụng đích, nhưng nó có thể dẫn đến sự kỳ quặc trong các điểm chuẩn vi mô nơi quá trình này cực kỳ ngắn ngủi.

Nếu bạn có một trường hợp thử nghiệm tối thiểu trong một quá trình ngắn ngủi, tôi khuyên bạn nên đóng main() bằng [[NSRunLoop currentLoop] run];. Điều đó sẽ chạy mãi mãi và cung cấp cho bạn thời gian chạy hữu hiệu cho mục đích của Instruments 'và/hoặc trình gỡ lỗi.

+0

Một điều kiện chủng tộc giữa chủ đề heap-watch-thread và chủ đề chính của chương trình của tôi, về cơ bản? Bởi vì đó là hai chủ đề/quy trình duy nhất mà tôi có thể tưởng tượng là có liên quan, vì tôi chưa tạo thêm bất kỳ chủ đề người dùng nào ... Điều lạ lùng là NSLog sửa nó nhưng gọi CFShow (CFStr ("something")) thì không, có nghĩa là nếu đó là một điều kiện chủng tộc, NSLog sẽ chậm hơn rất nhiều so với CFShow. :-) –

+0

ngủ (100) (100 giây) chỉ có thể là một chút quá mức cần thiết. :-) Thay đổi để ngủ cho 100 msec, ở hai nơi mà tôi hình cụ có thể thích một thời gian để bắt kịp với hành động. Tăng gấp đôi nó và thêm một mươi một, nếu bạn thích, độc giả thân yêu trong tương lai. –

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