2009-07-18 27 views
5

Tôi đã xem qua các câu hỏi được hỏi trên StackOverflow, nhưng có rất nhiều về quản lý bộ nhớ trong Objective-C mà tôi không thể tìm thấy câu trả lời tôi đang tìm kiếm.Sử dụng tự động sửa trước khi thêm đối tượng vào bộ sưu tập?

Câu hỏi đặt ra là nếu nó là ok (và được khuyến nghị) để gọi tự động trả trước khi thêm đối tượng mới được tạo vào bộ sưu tập (như NSMutableArray)? Hoặc tôi nên phát hành nó một cách rõ ràng sau khi thêm nó. (Tôi biết NSMutableArray willl giữ lại các đối tượng)

này minh họa câu hỏi của tôi:

Kịch bản A (autorelease):

- (void) add { 
    // array is an instance of NSMutableArray 

    MyClass *obj = [[[MyClass alloc] init] autorelease]; 

    [array addObject:obj]; 
} 

Kịch bản B (phát hành rõ ràng):

- (void) add { 
    // array is an instance of NSMutableArray 

    MyClass *obj = [[MyClass alloc] init]; 

    [array addObject:obj]; 

    [obj release]; 
} 

Tôi giả sử cả hai đều đúng, nhưng tôi không chắc chắn, và tôi chắc chắn không biết những gì cách preffered được.

Chuyên gia mục tiêu-C có thể làm sáng tỏ điều này không?

Trả lời

7

Cả hai đều chính xác và sẽ hoạt động như bạn mong đợi.

Cá nhân tôi thích sử dụng phương pháp thứ hai, nhưng chỉ vì tôi muốn rõ ràng về thời điểm các đối tượng được phát hành. Bằng cách tự động phát hiện đối tượng, tất cả những gì chúng tôi đang làm là nói "đối tượng này sẽ được giải phóng tại một số điểm tùy ý trong tương lai." Điều đó có nghĩa là bạn có thể đặt đối tượng autoreleased vào mảng, phá hủy mảng và đối tượng có thể (có thể) vẫn tồn tại.

Với phương pháp thứ hai, đối tượng sẽ bị hủy ngay lập tức với mảng (cung cấp không có gì khác đi cùng và giữ lại trong thời gian chờ đợi). Nếu tôi đang ở trong một môi trường bị hạn chế về bộ nhớ (ví dụ, iPhone), nơi tôi cần phải cẩn thận về lượng bộ nhớ tôi đang sử dụng, tôi sẽ sử dụng phương pháp thứ hai để tôi không có nhiều đối tượng kéo dài trong một NSAutoreleasePool ở đâu đó. Nếu sử dụng bộ nhớ không phải là một mối quan tâm lớn cho bạn (và nó thường không phải là cho tôi, hoặc là), thì một trong hai phương pháp là hoàn toàn chấp nhận được.

+2

Nếu bạn muốn mã của mình hoàn toàn an toàn, bạn nên sử dụng tự động phát hiện: Xem http://stackoverflow.com/questions/1147785/use-autorelease-before-adding-objects-to-a-collection/1149040#1149040 – Casebash

3

Cả hai đều chính xác nhưng B có thể được ưa thích hơn vì không có phí. Tự động làm cho nhóm tự động trả phí chịu trách nhiệm về đối tượng. Điều này có một chi phí rất nhỏ mà, tất nhiên, được nhân với số lượng các đối tượng liên quan.

Vì vậy, với một đối tượng A và B ít nhiều giống nhau nhưng chắc chắn không sử dụng A trong các trường hợp có nhiều đối tượng để thêm vào mảng.

Trong các trường hợp khác nhau, tự động phát hiện có thể trì hoãn và tích lũy giải phóng nhiều đối tượng ở cuối chuỗi. Điều này có thể là phụ tối ưu. Hãy cẩn thận rằng dù sao tự động phát hiện xảy ra rất nhiều mà không có sự can thiệp rõ ràng. Ví dụ: nhiều getters được triển khai theo cách này:

return [[myObject retain] autorelease]; 

vì vậy bất cứ khi nào bạn gọi cho getter, bạn thêm đối tượng vào nhóm tự động.

+0

"autorelease làm cho hồ bơi autorelease phụ trách của đối tượng. " Điều này không đúng. Các hồ bơi autorelease là không có gì nhiều hơn một cơ sở nhắn tin chậm trễ. Các hồ bơi autorelease không "chịu trách nhiệm" của một đối tượng, nó chỉ liệt kê nó để nhận được một tin nhắn -release trong tương lai. – NSResponder

+0

Với "chịu trách nhiệm", tôi có nghĩa là chính xác điều đó. Tôi sinh ra ở EU nhưng không phải ở Anh, lỗi của tôi. – IlDan

0

Bạn có thể gửi thông báo autorelease tại bất kỳ thời điểm nào vì nó không được kích hoạt cho đến khi vòng lặp thông báo của ứng dụng lặp lại (tức là cho đến khi tất cả các phương pháp của bạn đã hoàn thành việc thực hiện theo phản hồi của người dùng).

http://macdevcenter.com/pub/a/mac/2001/07/27/cocoa.html?page=last&x-showcontent=text

+0

Bất kỳ lý do cụ thể nào cho downvote? –

0

Bạn có alloc 'ed đối tượng, sau đó nó là công việc của bạn để phát hành nó tại một số điểm. Cả hai đoạn mã hoạt động giống hệt nhau, cách chính xác, với cách autorelease là đối tác cố tình chậm hơn.

Nói riêng, tôi thích cách autorelease vì việc nhập và gần như không bao giờ là nút cổ chai.

0

Cả hai đều OK. Một số người sẽ nói với bạn để tránh autorelease vì "trên cao" hoặc một số điều như vậy, nhưng sự thật là, có thực tế không có chi phí. Hãy tiếp tục và đánh giá nó và cố gắng tìm "overhead". Lý do duy nhất bạn tránh nó là trong một tình huống thiếu bộ nhớ như trên iPhone. Trên OS X, bạn có bộ nhớ thực tế không giới hạn, vì vậy nó sẽ không tạo ra nhiều sự khác biệt. Chỉ cần sử dụng cái nào thuận tiện nhất cho bạn.

+1

Nó không được cắt như khô. Nếu các đối tượng đang được tự động phát hành trong một vòng lặp, nó có thể cho số lượng lớn các đối tượng thêm để có được trái xung quanh cho đến khi một hồ chứa autorelease kèm theo được thoát. Điều này đôi khi có thể gây ra chi phí bộ nhớ lớn và chậm lại. (Vâng, điều đó đã xảy ra với tôi.) Tuy nhiên, điều này không phổ biến. Thông thường bạn có thể bỏ qua vấn đề cho đến khi nó xảy ra, và sau đó nhìn vào những gì đang xảy ra trong vòng lặp và tối ưu hóa nó bằng cách sử dụng các bản phát hành rõ ràng. –

+1

Tất cả những gì yêu cầu là quản lý đặc biệt của bể tự động. Nó vẫn không có nghĩa là autorelease là một điều xấu, đặc biệt là không phải trong trường hợp chung. – Chuck

11

IMHO, theo cách nào là 'đúng' là vấn đề ưu tiên. Tôi không đồng ý với những người phản hồi, những người ủng hộ việc không sử dụng autorelease, nhưng ưu tiên của tôi là sử dụng autorelease trừ khi có lý do thuyết phục. Tôi sẽ liệt kê các lý do của tôi và bạn có thể quyết định có thích hợp với phong cách lập trình của bạn hay không.

Như Chuck đã chỉ ra, có một huyền thoại bán đô thị có một số loại phí sử dụng các bể tự động. Điều này không thể được tiếp tục từ sự thật, và điều này xuất phát từ vô số giờ sử dụng Shark.app để bóp phần cuối của hiệu suất ra khỏi mã. Cố gắng tối ưu hóa cho điều này là sâu vào lãnh thổ "tối ưu hóa sớm". Nếu, và chỉ khi, Shark.app cung cấp cho bạn dữ liệu cứng rằng điều này có thể là một vấn đề, bạn thậm chí nên xem xét tìm kiếm nó.

Như những người khác đã chỉ ra, một đối tượng được tự động phát hành được "phát hành tại một số điểm sau này". Điều này có nghĩa là họ linger xung quanh, chiếm bộ nhớ, cho đến khi "điểm sau" cuộn xung quanh. Đối với "hầu hết" trường hợp, đây là ở dưới cùng của một quá trình xử lý sự kiện trước khi vòng lặp chạy ngủ cho đến khi sự kiện tiếp theo (bộ đếm thời gian, người dùng nhấp vào một cái gì đó, vv).

Thỉnh thoảng, bạn sẽ cần phải loại bỏ những vật thể tạm thời đó sớm hơn là sau này. Ví dụ, bạn cần xử lý một tệp lớn, nhiều megabyte hoặc hàng chục nghìn hàng từ một cơ sở dữ liệu. Khi điều này xảy ra, bạn sẽ cần đặt một số NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; tại một điểm được chọn tốt, theo sau là [pool release]; ở dưới cùng. Điều này hầu như luôn luôn xảy ra trong một số loại "vòng lặp xử lý hàng loạt", vì vậy nó thường ở đầu và cuối của một số vòng lặp quan trọng. Một lần nữa, điều này nên được dựa trên bằng chứng, không dựa trên linh cảm. Object.It của Instrument.app là những gì bạn sử dụng để tìm những điểm rắc rối này.

Lý do chính tại sao tôi thích autorelease để release, tuy nhiên, là nó là nhiều dễ dàng hơn để viết các chương trình bị rò rỉ-miễn phí. Nói tóm lại, nếu bạn chọn để đi con đường release, bạn cần phải đảm bảo rằng release là cuối cùng gửi đến obj, dưới tất cả hoàn cảnh. Trong khi điều này có vẻ như nó có thể là đơn giản, nó thực sự là đáng ngạc nhiên khó làm trong thực tế. Hãy ví dụ của bạn, ví dụ:

// array is an instance of NSMutableArray 
    MyClass *obj = [[MyClass alloc] init]; 
    [array addObject:obj]; 
    // Assume a few more lines of work.... 
    [obj release]; 

Bây giờ tưởng tượng rằng đối với một số lý do, một cái gì đó, ở đâu đó, tinh tế vi phạm giả định của bạn mà array là có thể thay đổi, có lẽ là kết quả của việc sử dụng một số phương pháp để xử lý các kết quả, và quay trở lại mảng chứa kết quả đã xử lý được tạo dưới dạng NSArray.Khi bạn gửi addObject: đến số NSArray bất biến đó, một ngoại lệ sẽ bị ném và bạn sẽ không bao giờ gửi obj thông báo release của mình. Hoặc có thể họ gặp khó khăn ở đâu đó giữa khi objalloc d và cần cuộc gọi đến release, như bạn kiểm tra một số điều kiện và ngay lập tức return() do nhầm lẫn vì nó trượt tâm trí của bạn rằng cuộc gọi đến release sau phải diễn ra.

Bạn vừa mới rò rỉ một đối tượng. Và có lẽ đã ký cho mình vài ngày cố gắng tìm ra nơi nào và tại sao nó lại bị rò rỉ nó. Từ kinh nghiệm, bạn sẽ dành nhiều giờ nhìn vào mã trên, tin rằng nó không thể là nguồn rò rỉ bởi vì bạn rất rõ ràng gửi obj một release. Sau đó, sau vài ngày, bạn sẽ kinh nghiệm những gì chỉ có thể được mô tả như là một biểu hiện tôn giáo khi bạn chứng ngộ nguyên nhân của vấn đề.

Hãy xem xét các trường hợp autorelease:

// array is an instance of NSMutableArray 
    MyClass *obj = [[[MyClass alloc] init] autorelease]; 
    [array addObject:obj]; 
    // Assume a few more lines of work.... 

Bây giờ, nó không còn vấn đề gì xảy ra bởi vì nó hầu như không thể bị rò rỉ obj vô tình, ngay cả dưới góc trường hợp cực kỳ bất thường hoặc đặc biệt.

+0

Điều này xuất hiện trong câu hỏi mới của tôi ở đây: http://stackoverflow.com/questions/2911678. Điểm tuyệt vời. –

+1

'[pool drain]' có vẻ là sự lựa chọn tốt hơn trong trường hợp iOS từng nhận được GC –

0

Tôi thích A (autoreleasing) cho ngắn gọn và "an toàn", như johne gọi nó. Nó đơn giản hóa mã của tôi và tôi chưa bao giờ gặp phải vấn đề với nó.

Tức là, cho đến hôm nay: Tôi gặp sự cố khi tự động phát hành một khối trước khi thêm nó vào một mảng. Xem câu hỏi stackoverflow tôi: [myArray addObject:[[objcBlock copy] autorelease]] crashes on dealloc'ing the array (Cập nhật: Hóa ra vấn đề là ở đâu đó trong mã của tôi, nhưng vẫn còn, đã có một sự khác biệt tinh tế trong cách cư xử với autorelease ...)

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