Nhóm tự động khôi phục lại NSAutoreleasePool hoạt động như thế nào?


95

Theo tôi được biết, bất cứ điều gì tạo ra với một alloc , mới , hoặc bản sao cần phải được phát hành bằng tay. Ví dụ:

int main(void) {
    NSString *string;
    string = [[NSString alloc] init];
    /* use the string */
    [string release];
}

Tuy nhiên, câu hỏi của tôi không phải là điều này có hợp lệ không ?:

int main(void) {
    NSAutoreleasePool *pool;
    pool = [[NSAutoreleasePool alloc] init];
    NSString *string;
    string = [[[NSString alloc] init] autorelease];
    /* use the string */
    [pool drain];
}

Câu trả lời:


68

Có, đoạn mã thứ hai của bạn hoàn toàn hợp lệ.

Mỗi khi -autorelease được gửi đến một đối tượng, nó sẽ được thêm vào nhóm autorelease bên trong nhất. Khi hồ bơi cạn nước, nó chỉ cần gửi -release đến tất cả các đối tượng trong hồ bơi.

Hồ bơi tự động gửi đơn giản chỉ là một sự tiện lợi cho phép bạn trì hoãn việc gửi - vui lòng cho đến "sau". Điều đó "sau" có thể xảy ra ở một số nơi, nhưng phổ biến nhất trong các ứng dụng Cocoa GUI là ở cuối chu kỳ vòng lặp chạy hiện tại.


5
đâu là cuối của chu kỳ vòng lặp chạy hiện tại, nếu tôi không có vòng lặp?
Cảm ơn

24
Không nên "bên ngoài-hầu hết" là "bên trong-hầu hết"?
Mike Weller

an objectnên được an object that is a subclass of NSObject or NSProxy and doesn't override -autorelease.

1
CHỈNH SỬA: Đã thay đổi từ bên ngoài sang bên trong nhất.
chakrit

1
Quan trọng: Nếu bạn sử dụng Đếm Tham chiếu Tự động (ARC), bạn không thể sử dụng trực tiếp các nhóm tự động vui lòng. Thay vào đó, bạn sử dụng các khối @autoreleasepool. Từ developer.apple.com/library/mac/#documentation/Cocoa/Reference/…
Md Mahbubur Rahman

37

NSAutoreleasePool: thoát so với phát hành

Vì chức năng của drainreleasedường như gây nhầm lẫn, nó có thể đáng được làm rõ ở đây (mặc dù điều này được đề cập trong tài liệu ...).

Nói đúng ra, từ quan điểm bức tranh lớn drainkhông tương đương với release:

Trong môi trường được tính tham chiếu, drainthực hiện các hoạt động tương tự như releasevậy, vì vậy cả hai tương đương nhau theo nghĩa đó. Để nhấn mạnh, điều này có nghĩa là bạn không bị rò rỉ hồ bơi nếu bạn sử dụng drainhơn là release.

Trong một môi trường thu gom rác, releaselà một điều không nên. Vì vậy, nó không có hiệu lực. drain, mặt khác, chứa một gợi ý cho bộ sưu tập rằng nó nên "thu thập nếu cần". Vì vậy, trong một môi trường thu gom rác, việc sử dụng draingiúp hệ thống cân bằng thu thập quét.


4
Căn bản là không thể 'rò rỉ' a NSAutoreleasePool. Điều này là do các pool hoạt động giống như một ngăn xếp. Việc khởi tạo một nhóm sẽ đẩy nhóm đó lên trên cùng của ngăn xếp nhóm tự động khôi phục chuỗi đó. -releasekhiến nhóm đó bật ra khỏi ngăn xếp bất kỳ nhóm nào được đẩy lên trên nó, nhưng vì lý do gì đó không được bật lên.
johne

7
Theo cách nào thì điều này có liên quan đến những gì tôi đã viết?
mmalc

2
Tôi thích cách anh ấy dành thời gian để tô đậm AND. SNAP!
Billy Grey

17

Như đã chỉ ra, đoạn mã thứ hai của bạn là đúng.

Tôi muốn đề xuất một cách ngắn gọn hơn để sử dụng nhóm autorelease hoạt động trên tất cả các môi trường (đếm tham chiếu, GC, ARC) và cũng tránh nhầm lẫn thoát / giải phóng:

int main(void) {
  @autoreleasepool {
    NSString *string;
    string = [[[NSString alloc] init] autorelease];
    /* use the string */
  }
}

Trong ví dụ trên, vui lòng lưu ý khối @autoreleasepool . Điều này được ghi lại ở đây .


2
Xin lưu ý rằng không cho phép tự động khôi phục với ARC.
dmirkitanov

1
Để làm rõ, người ta phải sử dụng @autoreleasepoolkhối với ARC.
Simon

7

Không, bạn nhầm rồi. Tài liệu nêu rõ rằng trong điều kiện không phải GC, -drain tương đương với -release, nghĩa là NSAutoreleasePool sẽ không bị rò rỉ.


Tôi tự hỏi tại sao Xcode lại tạo mã bằng -drain nếu đúng như vậy. Tôi đã sử dụng -drain vì tôi nghĩ nó tương đương với -release dựa trên mã được tạo bởi Xcode.
James Sumners 16/09/08

1
Về cơ bản là không thể 'rò rỉ' a NSAutoreleasePool: developer.apple.com/mac/library/documentation/Cocoa/Conceptual/…
johne

0

Những gì tôi đọc được từ Apple: "Vào cuối khối nhóm tự động vui lòng, các đối tượng nhận được thông báo tự động vui lòng trong khối sẽ được gửi một thông báo phát hành — một đối tượng nhận được thông báo phát hành cho mỗi lần nó được gửi một thông báo tự động vui lòng trong khối. "

https://developer.apple.com/library/mac/documentation/cocoa/conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html


0

gửi autorelease thay vì phát hành tới một đối tượng sẽ kéo dài thời gian tồn tại của đối tượng đó ít nhất cho đến khi bản thân nó bị cạn (có thể lâu hơn nếu đối tượng được giữ lại sau đó). Một đối tượng có thể được đưa vào cùng một nhóm nhiều lần, trong trường hợp đó, nó sẽ nhận được thông báo phát hành cho mỗi lần nó được đưa vào nhóm.


-2

Có và không. Cuối cùng, bạn sẽ giải phóng bộ nhớ chuỗi nhưng "rò rỉ" đối tượng NSAutoreleasePool vào bộ nhớ bằng cách sử dụng thoát thay vì phát hành nếu bạn chạy điều này trong môi trường thu gom rác (không được quản lý bộ nhớ). "Rò rỉ" này chỉ đơn giản là làm cho cá thể của NSAutoreleasePool "không thể truy cập" giống như bất kỳ đối tượng nào khác không có con trỏ mạnh trong GC và đối tượng sẽ bị xóa vào lần tiếp theo GC chạy, rất có thể ngay sau lệnh gọi tới -drain:

cống

Trong môi trường thu gom rác, kích hoạt thu gom rác nếu bộ nhớ được cấp phát kể từ lần thu thập cuối cùng lớn hơn ngưỡng hiện tại; nếu không thì hoạt động như một bản phát hành. ... Trong môi trường thu gom rác, phương thức này cuối cùng sẽ gọi objc_collect_if_needed.

Nếu không, nó tương tự như cách -releasecư xử của không phải GC, vâng. Như những người khác đã nêu, đó -releaselà điều kiện cấm theo GC, vì vậy, cách duy nhất để đảm bảo nhóm hoạt động đúng theo GC là thông qua -drain-draindưới không phải GC hoạt động chính xác như -releasedưới không phải GC và được cho là truyền đạt chức năng của nó rõ ràng hơn như tốt.

Tôi nên chỉ ra rằng câu lệnh của bạn "bất cứ thứ gì được gọi với new, cert hoặc init" không nên bao gồm "init" (nhưng nên bao gồm "copy"), bởi vì "init" không cấp phát bộ nhớ, nó chỉ thiết lập đối tượng (hàm tạo thời trang). Nếu bạn nhận được một đối tượng cấp phát và hàm của bạn chỉ được gọi là init, bạn sẽ không giải phóng nó:

- (void)func:(NSObject*)allocd_but_not_init
{
    [allocd_but_not_init init];
}

Điều đó không tiêu tốn thêm bất kỳ bộ nhớ nào so với bộ nhớ mà bạn đã bắt đầu (giả sử init không khởi tạo các đối tượng, nhưng bạn không chịu trách nhiệm về những điều đó).


Tôi không cảm thấy thoải mái khi để câu trả lời này được chấp nhận khi thông tin của bạn về cống không chính xác. Hãy xem Cập nhật developer.apple.com/documentation/Cocoa/Reference/Foundation/… và tôi sẽ chấp nhận lại.
James Sumners

Điều gì là không chính xác về câu trả lời? Trong môi trường thu gom rác (như đã nêu), thoát không xóa AutoReleasePool, vì vậy bạn sẽ làm rò rỉ bộ nhớ trừ khi bạn sử dụng bản phát hành. Trích dẫn tôi liệt kê là trực tiếp từ miệng con ngựa, các tài liệu về cống.
Loren Segal

1
Loren: Theo GC, - [Cống NSAutoreleasePool] sẽ kích hoạt một bộ sưu tập. -retain, -release và -autorelease đều bị người thu mua bỏ qua; đó là lý do tại sao -drain được sử dụng trên các nhóm tự động phát hành theo GC.
Chris Hanson

Trong tài liệu về 'thoát': Trong môi trường bộ nhớ được quản lý, điều này hoạt động giống như gọi bản phát hành. Vì vậy, bạn sẽ không bị rò rỉ bộ nhớ nếu bạn sử dụng 'thoát' thay vì phát hành.
mmalc 8/10/08

-[NSAutoreleasePool release]trong một môi trường thu gom rác là điều không nên. -[NSAutoreleasePool drain]hoạt động trong cả môi trường được đếm tham chiếu và thu thập rác.
Jonathan Sterling
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.