2013-02-13 27 views
19

Tôi đã phạm sai lầm trong khi tạo ra một TableView class, và vô tình giữ tôi @property như copy khi tôi định nghĩa nó:Tại sao thuộc tính NSMutableArray (bản sao, nonatomic) tạo NSArrays?

@property (copy, nonatomic) NSMutableArray *words; 

tôi khởi tạo mảng "đúng": (lưu ý đây là nỗ lực thứ ba, vì vậy hãy bỏ qua thực tế là tôi không sử dụng mutableCopy và cách khác tốt hơn để làm điều này)

NSArray *fixedWords = @[@"Eeny", @"Meeny", @"Miny", @"Moe", @"Catch", @"A", @"Tiger", @"By", @"His", @"Toe"]; 
NSMutableArray *mutWords = [[NSMutableArray alloc] initWithArray:fixedWords]; 
self.words = mutWords; 

Tuy nhiên khi tôi sau đó đến để sắp xếp lại mảng, nó đã bị rơi trên dòng removeObjectAtIndex:

id object = [self.words objectAtIndex:fromIndexPath.row]; 
NSUInteger from = fromIndexPath.row; 
NSUInteger to = toIndexPath.row; 
[self.words removeObjectAtIndex:from]; 

Với thông điệp lỗi

unrecognized selector sent to instance 

Đã rất nhiều đào bới để tìm ra rằng đây là vì sao có nghĩa là gán kết quả NSMutableArray trong việc tạo ra một tiêu chuẩn (nonmutable) NSArray. Bất cứ ai có thể giải thích lý do tại sao đây là hành vi chính xác?

+1

'@property (sao chép, nonatomic) NSMutableArray * từ;' không sử dụng bản sao, bạn sử dụng shuld giữ lại. –

+0

hoặc mạnh thay vì giữ lại –

+1

bản sao có thể có của [Sử dụng -mutableCopyWithZone: trên lớp tùy chỉnh khiến nó không thay đổi được] (http://stackoverflow.com/questions/14841130/using-mutablecopywithzone-on-custom-class-makes-it-immutable) –

Trả lời

31

bản sao, như được thực hiện bởi các lớp ca cao có thể thay đổi, luôn là returns their immutable counterparts. Vì vậy, khi một NSMutableArray được gửi bản sao, nó trả về một NSArray chứa các đối tượng tương tự.

words có vòng loại bộ nhớ copy, dòng này:

NSMutableArray *mutWords = [[NSMutableArray alloc] initWithArray:fixedWords]; 
self.words = mutWords; 

Mở rộng ra:

NSMutableArray *mutWords = [[NSMutableArray alloc] initWithArray:fixedWords]; 
self.words = [mutWords copy]; 

Cho rằng NSMutableArray là một lớp con của NSArray, trình biên dịch không phàn nàn, và bây giờ bạn có một quả bom đánh dấu trên tay vì NSArray không nhận ra các phương thức của lớp con có thể thay đổi được (vì nó không thể thay đổi nội dung của nó).

+4

Để rõ ràng: bản sao 'xảy ra trong quá trình thực hiện tổng hợp của bộ đặt, không phải tại trang cuộc gọi. – bbum

+0

Chỉ cần sửa đổi câu trả lời của tôi để minh họa tốt hơn điểm của bbum. – macserv

+1

@bbum Tất nhiên rồi. Đây chỉ là vì lợi ích ngắn gọn. Tôi thực sự không muốn nhận được vào những gì thuộc tính thực sự được cho loại câu hỏi :) – CodaFi

7

Thuộc tính không phải là huyền diệu, chúng chỉ là viết tắt. Khai báo @property trên đối tượng của bạn sẽ cho trình biên dịch tạo ra một biến thể sao lưu và các phương thức truy cập cho nó. Mã được tạo thực tế tùy thuộc vào các thuộc tính bạn đặt trên thuộc tính của mình.

Điều quan trọng cần nhớ là việc đặt thuộc tính sử dụng cú pháp dấu chấm cũng là viết tắt. Khi bạn gọi ...

self.words = mutWords; 

... bạn đang thực sự gọi phương thức tạo accessor đằng sau hậu trường, như thế này:

[self setWords:mutWords]; 

Kể từ khi bạn đã xác định các copy thuộc tính trên tài sản của bạn, bạn đã nói với trình biên dịch để tạo ra phương thức truy cập -setWords: có mã giống như thế này:

- (void)setWords:(NSMutableArray *)words 
{ 
    _words = [words copy]; 
} 

Hiểu tất cả điều đó, bạn có thể thấy điều gì đang xảy ra ng: phương thức setter được tạo sẽ gọi -copy trên đối số đầu vào và gán kết quả cho biến thể hiện sao lưu. Vì phương thức -copy luôn được triển khai để trả về đối tượng không thể thay đổi (thực hiện [aMutableString copy] sẽ trả về NSString, v.v.) nên thuộc tính sẽ luôn lưu trữ bản sao không thể thay đổi.

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