2010-01-19 17 views
7

Điều này tạo ra một đối tượng chuỗi bất biến:Ca cao: Thử nghiệm để tìm xem NSString có thay đổi hoặc có thể thay đổi không?

NSString* myStringA = @"A"; //CORRECTED FROM: NSMutableString* myStringA = @"A"; 

này tạo ra một đối tượng chuỗi có thể thay đổi:

NSMutableString* myStringB = [NSMutableString stringWithString:@"B"]; 

Nhưng cả hai đối tượng được ghi nhận theo cùng một loại đối tượng, "NSCFString":

NSLog(@"myStringA is type: %@, myStringB is type: %@", 
[myStringA class], [myStringB class]); 

Vì vậy, những gì phân biệt các đối tượng này trong nội bộ và làm cách nào để kiểm tra điều đó, để tôi có thể dễ dàng xác định xem biến chuỗi bí ẩn có bị biến đổi hay không có thể hoặc có thể thay đổi trước khi làm điều gì đó xấu xa với nó?

+0

Mã của Philippe dưới đây - nếu ([myStringB isKindOfClass: [NSMutableString class]]) - hoạt động và giải quyết vấn đề thực tế. – StringSection

+0

Tôi vẫn còn tò mò về sự khác biệt giữa một chuỗi bất biến và mutable được đại diện nội bộ, và nếu có thể được phát hiện trực tiếp (thực tế "loại đối tượng" in với NSLog). – StringSection

+1

Hiệu chỉnh: Tôi đã nhầm, mã - nếu ([myStringB isKindOfClass: [NSMutableString class]]) - không hoạt động sau khi tất cả. Nó trả về true cho dù chuỗi là NSString hoặc NSMutableString. Như Philippe chỉ ra dưới đây (trong câu trả lời đã được chỉnh sửa của anh ấy với mã khác), dường như không có cách nào được ghi lại để phát hiện các đối tượng có thể thay đổi và không thay đổi nói chung khi chạy. – StringSection

Trả lời

4

Không có cách nào (tài liệu) để xác định xem chuỗi có thể thay đổi khi chạy hay không.

Bạn mong chờ một trong những điều sau đây sẽ làm việc, nhưng không ai trong số họ làm việc:

[[s class] isKindOfClass:[NSMutableString class]]; // always returns false 
[s isMemberOfClass:[NSMutableString class]]; // always returns false 
[s respondsToSelector:@selector(appendString)]; // always returns true 

Thông tin thêm ở đây, mặc dù nó không giúp bạn với vấn đề này:

http://www.cocoabuilder.com/archive/cocoa/111173-mutability.html

+0

Không hoạt động: NSMutableString * lala = @ "lala"; \t if ([lala isKindOfClass: [NSMutableString class]]) NSLog (@ "Lớp ẩn"); - cho thấy thông điệp tường trình, tuy nhiên đối tượng là bất biến – Vladimir

+0

Tất nhiên nó không hoạt động, bởi vì mã là sai. ** NSMutableString * lala = @ "lala" ** là mã không hợp lệ. –

+0

Xin lỗi các bạn - NSMutableString * myStringA = @ "A"; là một lỗi đánh máy - sửa ngay trong tin nhắn gốc. – StringSection

3

Nếu bạn muốn kiểm tra các mục đích gỡ lỗi, mã sau sẽ hoạt động. Bản sao trên đối tượng không thay đổi được chính nó, trong khi đó là một bản sao thực sự cho các loại mutable, đó là những gì mã được dựa trên. Lưu ý rằng kể từ khi nó gọi copy nó chậm, nhưng nên được tốt để gỡ lỗi. Nếu bạn muốn kiểm tra bất kỳ lý do nào khác ngoài việc gỡ lỗi, hãy xem Rob trả lời (và quên nó đi).

BOOL isMutable(id object) 
{ 
    id copy = [object copy]; 
    BOOL copyIsADifferentObject = (copy != object); 
    [copy release]; 
    return copyIsADifferentObject; 
} 

Tuyên bố từ chối trách nhiệm: tất nhiên không đảm bảo rằng bản sao tương đương với việc giữ lại các loại không thể thay đổi. Bạn có thể chắc chắn rằng nếu isMutable trả về NO thì nó không thể thay đổi được vì vậy chức năng có thể được đặt tên là canBeMutable. Tuy nhiên, trong thế giới thực, đó là một giả định khá an toàn rằng các loại bất biến (NSString, NSArray) sẽ thực hiện tối ưu hóa này. Có rất nhiều mã ra bao gồm những thứ cơ bản như NSDictionary mà dự kiến ​​sẽ nhanh chóng sao chép từ các loại bất biến.

12

Tài liệu bao gồm lời giải thích khá dài về lý do tại sao Apple không muốn bạn thực hiện việc này và tại sao họ không hỗ trợ nó một cách rõ ràng trong Receiving Mutable Objects. Bản tóm tắt là:

Vì vậy, không đưa ra quyết định về đối tượng tính linh hoạt dựa trên nội dung nào cho bạn biết về một đối tượng. Xử lý các đối tượng có thể thay đổi hoặc không dựa trên những gì bạn được cung cấp tại ranh giới API (nghĩa là, dựa trên loại trả về ). Nếu bạn cần đánh dấu rõ ràng một đối tượng là có thể thay đổi hoặc không thay đổi được khi bạn vượt qua nó cho khách hàng, hãy chuyển thông tin đó làm cờ cùng với đối tượng.

Tôi tìm ví dụ NSView dễ hiểu nhất và minh họa một vấn đề cơ bản về Cocoa. Bạn có một NSMutableArray được gọi là "các phần tử" mà bạn muốn trưng ra như một mảng, nhưng không muốn người gọi gây rối. Bạn có một số tùy chọn:

  1. Hiển thị NSMutableArray của bạn dưới dạng NSArray.
  2. Luôn tạo bản sao không thể thay đổi khi được yêu cầu
  3. Lưu các phần tử dưới dạng NSArray và tạo mảng mới mỗi lần biến đổi.

Tôi đã thực hiện tất cả những điều này tại các điểm khác nhau. # 1 là cho đến nay là giải pháp đơn giản và nhanh nhất. Nó cũng nguy hiểm, vì mảng có thể thay đổi sau lưng của người gọi. Nhưng Apple cho biết đó là những gì họ làm trong một số trường hợp (lưu ý cảnh báo cho -subviews trong NSView). Tôi có thể xác nhận rằng trong khi # 2 và # 3 an toàn hơn nhiều, họ có thể tạo ra các vấn đề hiệu suất lớn, đó có thể là lý do tại sao Apple đã chọn không sử dụng chúng trên các thành viên được truy cập như -subviews.

Kết quả của tất cả những điều này là nếu bạn sử dụng # 1, thì nội tâm sẽ đánh lừa bạn. Bạn có một NSMutableArray cast như là một NSArray, và introspection sẽ chỉ ra rằng nó là mutable (introspection không có cách nào để biết cách khác). Nhưng bạn không được biến đổi nó. Chỉ kiểm tra kiểu biên dịch thời gian mới có thể cho bạn biết điều đó và vì vậy đó là điều duy nhất bạn có thể tin tưởng.

Việc khắc phục cho điều này sẽ là một loại phiên bản không thể ghi đè nhanh trên bản ghi của cấu trúc dữ liệu có thể thay đổi. Cách đó # 2 có thể được thực hiện với hiệu suất tốt. Tôi có thể tưởng tượng những thay đổi cho cụm NSArray cho phép điều này, nhưng nó không tồn tại trong Cocoa ngày nay (và có thể tác động đến hiệu năng NSArray trong trường hợp bình thường, làm cho nó không khởi động). Ngay cả khi chúng tôi đã có nó, có lẽ quá nhiều mã ra khỏi đó dựa vào hành vi hiện tại để bao giờ cho phép sự mẫn cảm đột biến được tin cậy.

+1

Bạn có thể giải quyết vấn đề về hiệu suất với # 3 bằng cách triển khai và trưng ra các trình truy cập mảng đột biến (http://developer.apple.com/documentation/Cocoa/Conceptual/ModelObjects/Articles/moAccessorMethods.html) và sử dụng chúng trong các lớp khác. Bằng cách đó, truy cập trực tiếp vào mảng có thể thay đổi vẫn là riêng tư, nhưng bạn không tạo và ném đi các mảng tạm thời. –

+1

@PH Điều này đúng và tôi nên có giải pháp KVC. Vấn đề nó đặt ra là có rất nhiều trường hợp người gọi thực sự cần một bộ sưu tập (thường là để vượt qua một cái gì đó khác mà đòi hỏi một). Tôi đã thường giải quyết điều này bằng cách phơi bày một cái gì đó như elementEnumerator, mà người gọi có thể biến thành một ảnh chụp nếu anh ta muốn. Vấn đề với cách tiếp cận này là số lượng mã bổ sung cần thiết cho cả người gọi và người gọi. Đó là khai sáng rằng Apple, ngay cả trong mã mới cho UIView, đã chọn để chỉ lộ NSMutableArray "subviews" như là một NSArray và không cung cấp đầy đủ KVC. –

+0

Hm? Tôi không rõ ràng về vấn đề bạn đang nói về hoặc giải pháp của bạn là gì. Bài đăng trên blog và/hoặc ghi đè? –

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