Quan sát một NSMutableArray để chèn / loại bỏ


76

Một lớp có thuộc tính (và instance var) kiểu NSMutableArray với các trình truy cập tổng hợp (thông qua @property). Nếu bạn quan sát mảng này bằng cách sử dụng:

Và sau đó chèn một đối tượng vào mảng như sau:

Thông báo ObserValueForKeyPath ... không được gửi. Tuy nhiên, những điều sau sẽ gửi thông báo thích hợp:

Điều này là do mutableArrayValueForKeytrả về một đối tượng proxy có nhiệm vụ thông báo cho người quan sát.

Nhưng không phải các trình truy cập tổng hợp sẽ tự động trả về một đối tượng proxy như vậy? Cách thích hợp để giải quyết vấn đề này là gì - tôi có nên viết một trình truy cập tùy chỉnh mà chỉ gọi [super mutableArrayValueForKey...]không?

Câu trả lời:


79

Nhưng không phải các trình truy cập tổng hợp sẽ tự động trả về một đối tượng proxy như vậy?

Không.

Cách thích hợp để giải quyết vấn đề này là gì - tôi có nên viết một trình truy cập tùy chỉnh mà chỉ gọi [super mutableArrayValueForKey...]không?

Không. Triển khai trình truy cập mảng . Khi bạn gọi các cuộc gọi này, KVO sẽ tự động đăng các thông báo thích hợp. Vì vậy, tất cả những gì bạn phải làm là:

và Điều Đúng sẽ tự động xảy ra.

Để thuận tiện, bạn có thể viết một trình truy cập addTheArrayObject:. Trình truy cập này sẽ gọi một trong những trình truy cập mảng thực được mô tả ở trên:

(Bạn có thể và nên điền vào lớp thích hợp cho các đối tượng trong mảng, thay cho NSObject .)

Sau đó, thay vì [myObject insertObject:…], bạn viết[myObject addTheArrayObject:newObject] .

Đáng buồn thay, add<Key>Object:và đối tác của nóremove<Key>Object: , lần cuối cùng tôi kiểm tra, chỉ được KVO nhận dạng cho các thuộc tính set (như trong NSSet), không phải thuộc tính mảng, vì vậy bạn không nhận được thông báo KVO miễn phí với chúng trừ khi bạn triển khai chúng trên đầu các trình truy cập mà nó nhận ra . Tôi đã gửi một lỗi về điều này: x-radar: // problem / 6407437

Tôi có danh sách tất cả các định dạng bộ chọn công cụ truy cập trên blog của mình.


5
Vấn đề số 1 là khi bạn thêm một người quan sát, bạn đang quan sát một thuộc tính của một đối tượng nào đó. Mảng là giá trị của thuộc tính đó, không phải của chính thuộc tính. Đó là lý do tại sao bạn cần sử dụng trình truy cập hoặc -mutableArrayValueForKey: để sửa đổi mảng.
Chris Hanson

Điểm cuối cùng của bạn dường như đã lỗi thời - Tôi nhận được thông báo KVO miễn phí về các thuộc tính NSArray, miễn là tôi triển khai cả thêm và xóa trình truy cập.
Bryan

9

Tôi sẽ không sử dụng willChangeValueForKeydidChangeValueForKeytrong tình huống này. Đối với một, chúng có nghĩa là để chỉ ra giá trị tại đường dẫn đó đã thay đổi, không phải rằng các giá trị trong mối quan hệ với nhiều người đang thay đổi. Bạn sẽ muốn sử dụng willChange:valuesAtIndexes:forKey:thay thế, nếu bạn làm theo cách này. Mặc dù vậy, việc sử dụng các thông báo KVO thủ công như thế này là đóng gói không tốt. Một cách tốt hơn để làm điều đó là xác định một phương thức addSomeObject:trong lớp thực sự sở hữu mảng, phương thức này sẽ bao gồm các thông báo KVO thủ công. Bằng cách này, các phương thức bên ngoài đang thêm đối tượng vào mảng cũng không cần phải lo lắng về việc xử lý KVO của chủ sở hữu mảng, điều này sẽ không trực quan lắm và có thể dẫn đến mã không cần thiết và có thể có lỗi nếu bạn bắt đầu thêm đối tượng vào mảng từ một số nơi.

Trong ví dụ này, tôi thực sự sẽ tiếp tục sử dụng mutableArrayValueForKey:. Tôi không tích cực với mảng có thể thay đổi, nhưng tôi tin rằng từ việc đọc tài liệu rằng phương thức này thực sự thay thế toàn bộ mảng bằng một đối tượng mới, vì vậy nếu hiệu suất là mối quan tâm, bạn cũng sẽ muốn triển khai insertObject:in<Key>AtIndex:removeObjectFrom<Key>AtIndex:trong lớp sở hữu mảng .


7

khi bạn chỉ muốn quan sát số lượng đã thay đổi, bạn có thể sử dụng đường dẫn khóa tổng hợp:

nhưng lưu ý rằng bất kỳ sắp xếp lại nào trong Array sẽ không kích hoạt.


Hoặc thay thế cho vấn đề đó, ví dụ: khi [-replaceObjectAtIndex: withObject:] được sử dụng.
Patrick Pijnappel

3

Câu trả lời của riêng bạn cho câu hỏi của riêng bạn gần như đúng. Không cung cấp theArraybên ngoài. Thay vào đó, hãy khai báo một thuộc tính khác theMutableArray, tương ứng với không có biến phiên bản nào và viết trình truy cập này:

Kết quả là các đối tượng khác có thể sử dụng thisObject.theMutableArrayđể thực hiện thay đổi đối với mảng và những thay đổi này sẽ kích hoạt KVO.

Các câu trả lời khác chỉ ra rằng hiệu quả sẽ tăng lên nếu bạn cũng thực hiện insertObject:inTheArrayAtIndex:removeObjectFromTheArrayAtIndex:vẫn đúng. Nhưng không cần các đối tượng khác phải biết về những điều này hoặc gọi điện trực tiếp cho họ.


Khi sử dụng phím tắt này, tôi chỉ có thể quan sát các thay đổi trên đường dẫn phím "theArray", không phải "theMutableArray."
Michael Mior

Ngoài ra, mọi thứ hoạt động hoàn hảo và tôi có thể thao tác theMutableArraynhư thể nó là một NSMutableArraytài sản.
Michael Mior

Khi tôi cố gắng lười biếng init đối tượng proxy này, không có thông báo KVO nào được phát ra. Bạn có biết tại sao? - (NSMutableArray *) theMutableArray {if (_theMutableArray) return _theMutableArray; _theMutableArray = [self mutableArrayValueForKey: @ "theArray"]; return _theMutableArray; }
Lương Huy Đức

2

Nếu bạn không cần setter, bạn cũng có thể sử dụng biểu mẫu đơn giản hơn bên dưới, có hiệu suất tương tự ( tốc độ tăng trưởng tương tự trong các thử nghiệm của tôi) và ít bản ghi sẵn hơn.

Điều này hoạt động bởi vì nếu các trình truy cập mảng không được triển khai và không có bộ xác lập cho khóa, mutableArrayValueForKey:sẽ tìm kiếm một biến cá thể có tên _<key>hoặc <key>. Nếu nó tìm thấy một, proxy sẽ chuyển tiếp tất cả các thông báo đến đối tượng này.

Xem các tài liệu này của Apple , phần "Mẫu tìm kiếm công cụ truy cập cho các bộ sưu tập có thứ tự", # 3.


Vì một số lý do điều này không làm việc cho tôi. Tôi có thể thực sự mù trong trường hợp này.
Entalpi

1
Khi sử dụng giải pháp này, hãy chắc chắn để đánh dấu tài sản của bạn như readonly, nếu không đây sẽ là bị mắc kẹt trong một vòng lặp vô hạn ...
Symaxion

0

Bạn cần kết thúc addObject:cuộc gọi của mình willChangeValueForKey:didChangeValueForKey:các cuộc gọi. Theo như tôi biết, không có cách nào để NSMutableArray mà bạn đang sửa đổi biết về bất kỳ người quan sát nào đang theo dõi chủ nhân của nó.


0

một giải pháp là sử dụng NSArray và tạo nó từ đầu bằng cách chèn và xóa, như

so với bạn nhận được KVO và có thể so sánh mảng cũ và mới

LƯU Ý: self.myArray không được bằng nil, nếu không thì arrayByAddingObject: cũng cho kết quả bằng nil

Tùy thuộc vào từng trường hợp, đây có thể là giải pháp và vì NSArray chỉ lưu trữ các con trỏ, đây không phải là chi phí lớn, trừ khi bạn làm việc với các mảng lớn và các hoạt động thường xuyên

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.