KVO và ARC làm thế nào để loại bỏObserver


87

Làm cách nào để xóa một người quan sát khỏi một đối tượng trong ARC ? Có phải chúng ta chỉ thêm trình quan sát và quên xóa nó không? Nếu chúng ta không còn quản lý bộ nhớ theo cách thủ công thì chúng ta sẽ từ chức quan sát ở đâu?

Ví dụ: trên bộ điều khiển chế độ xem:

[self.view addObserver:self
            forKeyPath:@"self.frame"
               options:NSKeyValueObservingOptionNew 
               context:nil];

Trước đây, tôi sẽ gọi phương thức removeObserver:của bộ điều khiển chế độ xem dealloc.


4
Lưu ý rằng đó là một ý tưởng rất tồi với KVO .frame. Như đã được các kỹ sư của Apple viết trên StackOverflow, thuộc tính khung của UIKit không tuân thủ KVO. Khi nó hoạt động, đó chỉ là một sự tình cờ thuần túy.
steipete

2
KeyPath của bạn không nên @"frame"@"self.frame"?
Besi

Câu trả lời:


126

Bạn vẫn có thể triển khai -dealloctheo ARC, đây dường như là nơi thích hợp để loại bỏ việc quan sát các giá trị chính. Bạn chỉ không gọi [super dealloc]từ bên trong phương thức này nữa.

Nếu -releasetrước đây bạn coi trọng quá mức , bạn đã làm sai cách.


1
Bạn có chắc về điều này? Tôi trích dẫn từ clang.llvm.org/docs/… , mục 7.1.2. dealloc: "Lý do: mặc dù ARC tự động hủy các biến phiên bản, vẫn có những lý do chính đáng để viết một phương thức dealloc, chẳng hạn như giải phóng các tài nguyên không thể truy xuất. Việc không gọi được [super dealloc] trong một phương thức như vậy gần như luôn là một lỗi."
Elise van Looij

@ElisevanLooij Vâng, đúng vậy. Nếu bạn xuất phát từ lớp này, có vẻ như bạn phải gọi [super dealloc]. Ai khác nên làm điều này cho bạn.
Björn Landmesser

@ElisevanLooij Rất tiếc, đáng lẽ phải kiểm tra trước. Nó không được phép gọi [super dealloc]trong một phương thức dealloc. Không biết điều này sẽ hoạt động như thế nào sau đó khi phân lớp con của lớp được đề cập. Có lẽ nó chỉ là khuyến khích sử dụng finalizethay vì (nơi bạn gọi [super finalize])
Björn Landmesser

17
@ElisevanLooij - Điểm mà họ đang cố gắng thực hiện là liên quan đến trường hợp quản lý bộ nhớ thủ công. Bởi vì không gọi [super dealloc]cuối cùng trong phương pháp đó luôn là một lỗi trong quản lý bộ nhớ thủ công, trình biên dịch sẽ xử lý nó cho bạn ngay bây giờ, đó là lý do tại sao bạn không thể gọi -dealloctrực tiếp nữa. Những thứ duy nhất bạn đưa vào một -deallocphương thức trong ARC là bất kỳ tài nguyên không phải đối tượng nào bạn cần giải phóng hoặc các tác vụ dọn dẹp như xóa người quan sát. Từ ngữ họ sử dụng hơi lầy lội, nhưng đây là ý của họ.
Brad Larson

7
@ BjörnMilcke - Như tôi nhận xét về câu trả lời của Elise, -finalizeđược sử dụng cho điều này trong bộ sưu tập rác, nơi -deallockhông bao giờ được gọi, nhưng hoàn toàn có thể chấp nhận được khi đặt mã này trong -deallocARC. [super dealloc]được gọi tự động cho bạn, đó là lý do tại sao gọi nó trong ARC là một lỗi.
Brad Larson

1

Tôi làm điều đó với mã này

- (void)dealloc
{
@try{
    [self.uAvatarImage removeObserver:self forKeyPath:@"image" context:nil];
} @catch(id anException) {
    //do nothing, obviously it wasn't attached because an exception was thrown
}
}    

2
Điểm của xử lý ngoại lệ là deallocgì? Đã quá muộn để làm bất cứ điều gì về nó.
Abizern

Mục đích của việc loại bỏ người quan sát trên một biến cá thể trong dealloc là gì? UAvatarImage này sẽ sớm được phân bổ cùng với bất kỳ người quan sát nào mà nó đã đăng ký vào các đường dẫn chính của nó.
shoumikhin

1
@shoumikhin Tôi đang sử dụng ARC và tôi đã phải xóa trình quan sát trong phương pháp dealloc. Tôi có cùng một câu hỏi mà bạn có. Tuy nhiên, khi tôi chạy nhiều phiên bản của lớp, cuối cùng tôi gặp lỗi exc_bad_address. Làm điều này đã giải quyết vấn đề. Ngoài ra, câu trả lời từ đây stackoverflow.com/questions/32490808/… đã giúp tôi phát hiện ra vấn đề.
mac10688

-2

Ở những nơi khác về vấn đề tràn ngăn xếp, Chris Hanson khuyên bạn nên sử dụng phương thức finalize cho mục đích này và triển khai một phương thức vô hiệu hóa riêng biệt để chủ sở hữu có thể cho các đối tượng biết rằng chúng đã hoàn thành. Trong quá khứ, tôi đã nhận thấy các giải pháp của Hanson được suy nghĩ thấu đáo, vì vậy tôi sẽ tiếp tục với điều đó.


13
Lưu ý rằng anh ấy đang đề cập đến việc thu gom rác ở đó, không phải ARC (câu trả lời của anh ấy được viết vào năm 2008). Dưới bộ sưu tập rác, -deallockhông bao giờ được gọi. Trong ARC, nó là. Hoàn toàn có thể chấp nhận được nếu loại bỏ những người quan sát KVO -dealloc, như Chris Lattner (người biết anh ấy đang nói về điều gì) chỉ ra trong các diễn đàn dành cho nhà phát triển của Apple tại đây: devforums.apple.com/message/475850
Brad Larson

3
Cảm ơn Brad, vì đã làm tất cả công việc này. Không có để hoàn thành, có để dealloc nhưng không có [super dealloc]. Đơn giản thực sự, một khi bạn biết nó. Này, @drunknbass, chấp nhận câu trả lời của người đàn ông đó!
Elise van Looij
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.