2012-04-25 36 views
18

Khi biên dịch với ARC, các đối số phương pháp thường xuất hiện để được giữ lại ở đầu phương thức và được giải phóng ở cuối. Cặp giữ/giải phóng này dường như không cần thiết, và mâu thuẫn với ý tưởng rằng ARC "tạo ra mã mà bạn đã viết". Không ai trong những ngày tối, trước ARC này thực hiện thêm/giữ lại thêm trên tất cả các đối số phương pháp chỉ để được an toàn, đúng không?Tại sao ARC giữ lại các đối số phương thức?

xem xét:

@interface Test : NSObject 
@end 

@implementation Test 

- (void)testARC:(NSString *)s 
{ 
    [s length]; // no extra retain/release here. 
} 

- (void)testARC2:(NSString *)s 
{ 
    // ARC inserts [s retain] 
    [s length]; 
    [s length]; 
    // ARC inserts [s release] 
} 

- (void)testARC3:(__unsafe_unretained NSString *)s 
{ 
    // no retain -- we used __unsafe_unretained 
    [s length]; 
    [s length]; 
    // no release -- we used __unsafe_unretained 
} 

@end 

Khi biên soạn với Xcode 4.3.2 ở chế độ phát hành, lắp ráp (ví dụ mà tôi có thể hiểu được nó) chứa các cuộc gọi đến và objc_retainobjc_release lúc bắt đầu và kết thúc của phương pháp thứ hai. Chuyện gì vậy?

Đây không phải là vấn đề lớn, nhưng lưu lượng bổ sung/giải phóng thêm này sẽ hiển thị khi sử dụng Công cụ để lập hồ sơ mã nhạy cảm với hiệu suất. Có vẻ như bạn có thể trang trí các đối số phương thức với __unsafe_unretained để tránh việc giữ lại/giải phóng thêm, như tôi đã làm trong ví dụ thứ ba, nhưng làm như vậy cảm thấy khá kinh tởm.

+3

Bất kỳ ý tưởng nào tại sao nó không xảy ra trong ví dụ đầu tiên? – Thilo

+0

Bạn luôn có thể kiểm tra mã được tạo ra thực tế bằng cách sử dụng chế độ xem trợ lý tháo gỡ. – Danra

Trả lời

17

Xem this reply từ ObjC ngôn ngữ mailing list:

Khi trình biên dịch không biết gì về các hành vi quản lý bộ nhớ của một hàm hoặc phương pháp (và điều này xảy ra rất nhiều ), sau đó các Trình biên dịch phải giả định:

1) Chức năng hoặc phương pháp hoàn toàn có thể sắp xếp lại hoặc thay thế toàn bộ đồ thị đối tượng của ứng dụng (có thể không, nhưng có thể là ). 2) Người gọi có thể là mã số tham chiếu thủ công và do đó thời gian tồn tại trong các tham số không thực tế là có thể biết được.

Cho # 1 và # 2; và cho rằng ARC phải không bao giờ cho phép đối tượng được giải phóng sớm, sau đó hai giả định này buộc trình biên dịch để giữ lại được truyền trong đối tượng thường xuyên hơn không.

Tôi nghĩ rằng vấn đề chính là cơ thể của phương pháp của bạn có thể dẫn đến các đối số được phát hành, vì vậy ARC có hành động phòng thủ và giữ chân họ:

- (void) processItems 
{ 
    [self setItems:[NSArray arrayWithObject:[NSNumber numberWithInt:0]]]; 
    [self doSomethingSillyWith:[items lastObject]]; 
} 

- (void) doSomethingSillyWith: (id) foo 
{ 
    [self setItems:nil]; 
    NSLog(@"%@", foo); // if ARC did not retain foo, you could be in trouble 
} 

Đó cũng có thể là lý do mà bạn không thấy phần giữ lại thêm khi chỉ có một cuộc gọi duy nhất trong phương thức của bạn.

+5

Tóm lại, trong ví dụ thứ hai, lệnh gọi đầu tiên tới '[s length]' có thể khiến 's' bị deallocated trừ khi nó được giữ lại trước, do đó cuộc gọi thứ hai có thể là một đối tượng không tồn tại. – JeremyP

+0

"trình biên dịch không biết gì về hành vi quản lý bộ nhớ của một hàm hoặc phương thức". Người ta sẽ nghĩ rằng nó có thể tìm ra (hoặc được nói bởi Apple) rằng NSString: chiều dài là cư xử tốt. Có ví dụ khi trình biên dịch có đủ thông tin hữu ích không? Hay điều này thực sự xảy ra với các cuộc gọi phương thức * all *? – Thilo

+0

Tôi đoán quá khó để đáng giá. "Hành xử tốt" có lẽ sẽ sôi xuống để "không giải phóng bất cứ điều gì ngoài các biến địa phương", và đó là một sự bảo đảm khá mạnh mẽ để thực hiện. Chi phí của việc giữ lại và phát hành thêm trên các đối số phương thức là không hiệu quả đối với hầu hết mọi người và khi nó thực sự gây tổn hại cho bạn, '__unsafe_unretained' giải quyết trường hợp nếu bạn biết mình đang làm gì. – zoul

1

Nó sẽ không tăng sau hậu trường. Dưới ARC nếu đối tượng là Strong nó sẽ chỉ đơn giản là vẫn còn sống cho đến khi không có con trỏ mạnh mẽ hơn cho nó. Nhưng điều này thực sự không có gì để làm với đối tượng được thông qua như một tham số hay không.

2

Việc truyền dưới dạng tham số không, nói chung, hãy tăng số lần giữ lại. Tuy nhiên, nếu bạn chuyển nó đến một cái gì đó như NSThread, nó được ghi cụ thể là nó sẽ giữ lại tham số cho chuỗi mới.

Vì vậy, không có ví dụ về cách bạn định bắt đầu chuỗi mới này, tôi không thể đưa ra câu trả lời dứt khoát. Nói chung, mặc dù, bạn nên được tốt.

2

Ngay cả những câu trả lời của linh hồn là đúng, nó là sâu hơn một chút so với nó nên là:

Nó được giữ lại, bởi vì các tài liệu tham khảo thông qua được gán cho một biến mạnh mẽ, biến tham số. Điều này và chỉ có điều này là lý do cho cặp giữ/giải phóng. (Đặt tham số var thành __weak và điều gì sẽ xảy ra?)

Bạn có thể tối ưu hóa tham số đó? Nó sẽ giống như tối ưu hóa mọi cặp giữ/giải phóng trên các biến cục bộ, bởi vì các tham số là các biến cục bộ. Điều này có thể được thực hiện, nếu trình biên dịch hiểu mã lỗ bên trong phương thức bao gồm tất cả các tin nhắn được gửi và các cuộc gọi chức năng. Điều này có thể được áp dụng mà hiếm khi tiếng kêu đó thậm chí không cố gắng làm điều đó. (Hãy tưởng tượng rằng arg trỏ đến một người (chỉ) thuộc về một nhóm và nhóm là dealloc'd: người đó cũng sẽ được dealloc'd.)

Và có, không giữ lại arg trong MRC là một loại nguy hiểm, nhưng thông thường các nhà phát triển biết mã của họ tốt, rằng họ đã tối ưu hóa việc giữ lại/giải phóng mà không cần suy nghĩ về nó.

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