137

Tôi mới sử dụng chương trình Mac/iPhone và Objective-C. Trong C# và Java, chúng ta có "generics", các lớp collection mà các thành viên của nó chỉ có thể thuộc loại được khai báo. Ví dụ, trong C#Có bộ sưu tập mạnh mẽ nào trong Objective-C không?

Dictionary<int, MyCustomObject>

chỉ có thể chứa từ khóa là các số nguyên và giá trị mà là loại MyCustomObject. Có một cơ chế tương tự tồn tại trong Objective-C không?

+0

Chỉ mới bắt đầu tìm hiểu về ObjC. Có lẽ bạn có thể sử dụng ObjC++ để thực hiện việc nâng hạng nặng? – Toybuilder

+2

ObjC++ không thực sự là một ngôn ngữ ... chỉ là một cách để tham khảo khả năng của ObjC để xử lý C++ inline giống như nó sẽ xử lý C.Bạn không nên làm điều này trừ khi bạn phải, mặc dù (chẳng hạn như nếu bạn cần sử dụng thư viện của bên thứ ba được viết bằng C++). –

+0

Khá nhiều bản sao chính xác của http://stackoverflow.com/questions/649483/is-there-any-way-to-enforce-typing-on-nsarray-nsmutablearray-etc –

Trả lời

205

Trong Xcode 7, Apple đã giới thiệu 'Lightweight Generics' thành Objective-C. Trong Objective-C, chúng sẽ tạo ra các cảnh báo trình biên dịch nếu có một kiểu không phù hợp.

NSArray<NSString*>* arr = @[@"str"]; 

NSString* string = [arr objectAtIndex:0]; 
NSNumber* number = [arr objectAtIndex:0]; // Warning: Incompatible pointer types initializing 'NSNumber *' with an expression of type 'NSString *' 

Và trong Swift code, họ sẽ tạo ra một lỗi biên dịch:

var str: String = arr[0] 
var num: Int = arr[0] //Error 'String' is not convertible to 'Int' 

Generics Lightweight đang dự định sẽ được sử dụng với NSArray, NSDictionary và NSSet, nhưng bạn cũng có thể thêm chúng vào các lớp học của riêng bạn :

@interface GenericsTest<__covariant T> : NSObject 

-(void)genericMethod:(T)object; 

@end 

@implementation GenericsTest 

-(void)genericMethod:(id)object {} 

@end 

Mục tiêu-C sẽ hoạt động như trước đây với cảnh báo trình biên dịch.

GenericsTest<NSString*>* test = [GenericsTest new]; 

[test genericMethod:@"string"]; 
[test genericMethod:@1]; // Warning: Incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString *' 

nhưng Swift sẽ bỏ qua thông tin chung hoàn toàn. (Không còn đúng trong Swift 3+.)

var test = GenericsTest<String>() //Error: Cannot specialize non-generic type 'GenericsTest' 

Aside from than these Foundation collection classes, Objective-C lightweight generics are ignored by Swift. Any other types using lightweight generics are imported into Swift as if they were unparameterized.

Interacting with Objective-C APIs

+0

Vì tôi có câu hỏi về generics và các loại trả lại trong các phương pháp tôi đã hỏi câu hỏi của mình trong các chủ đề khác nhau, để giữ cho mọi thứ rõ ràng: http://stackoverflow.com/questions/30828076/objective-c-generics-not-working-for-methods – lvp

+0

Bạn chỉ có thể sử dụng điều này với Xcode 7? – rizzes

+2

@rizzes. Vâng, nó vừa được giới thiệu. – connor

6

Không có generics trong Objective-C.

From the Docs

Arrays are ordered collections of objects. Cocoa provides several array classes, NSArray, NSMutableArray (a subclass of NSArray), and NSPointerArray.

91

This answer is outdated but remains for historical value. As of Xcode 7, Connor's answer from Jun 8 '15 is more accurate.


Không, không có Generics trong Objective-C, trừ khi bạn muốn sử dụng C++ mẫu trong bộ sưu tập các lớp học riêng của bạn (mà tôi mạnh mẽ khuyến khích).

Mục tiêu-C có tính năng nhập động dưới dạng đối tượng địa lý, có nghĩa là thời gian chạy không quan tâm đến loại đối tượng vì tất cả các đối tượng đều có thể nhận tin nhắn. Khi bạn thêm một đối tượng vào bộ sưu tập tích hợp, chúng sẽ được coi như là loại id. Nhưng đừng lo lắng, chỉ cần gửi tin nhắn cho những đối tượng như bình thường; nó sẽ hoạt động tốt (trừ khi tất nhiên một hoặc nhiều đối tượng trong bộ sưu tập không trả lời thư bạn đang gửi).

Generics là cần thiết trong các ngôn ngữ như Java và C# bởi vì chúng là các ngôn ngữ mạnh, tĩnh. Hoàn toàn khác biệt so với tính năng đánh máy năng động của Objective-C.

+87

Tôi không đồng ý với "đừng lo lắng, chỉ cần gửi tin nhắn cho những đối tượng đó". Nếu bạn đặt nhầm loại đối tượng vào bộ sưu tập, mà không trả lời những thông báo này, điều này sẽ mang lại lỗi thời gian chạy. Sử dụng Generics trong các ngôn ngữ khác tránh được vấn đề này với việc kiểm tra thời gian biên dịch. – henning77

+8

@ henning77 Có, nhưng Mục tiêu-C là một ngôn ngữ năng động hơn các ngôn ngữ này. Nếu bạn muốn loại an toàn mạnh mẽ, hãy sử dụng các ngôn ngữ đó. –

+36

Tôi cũng không đồng ý với triết lý không lo lắng - ví dụ: nếu bạn kéo mục đầu tiên ra khỏi NSArray và đưa nó vào NSNumber nhưng mục đó thực sự là một NSString, bạn bị vặn ... – jjxtra

11

Không, nhưng để làm cho nó rõ ràng hơn bạn có thể bình luận nó với các loại đối tượng bạn muốn để lưu trữ, tôi đã nhìn thấy điều này thực hiện một vài lần khi bạn cần phải viết một cái gì đó trong Java 1,4 ngày nay) ví dụ:

NSMutableArray* /*<TypeA>*/ arrayName = .... 

hoặc

NSDictionary* /*<TypeA, TypeB>*/ dictionaryName = ... 
+0

Tôi đoán đây là một cách hay để có tài liệu, trong trường hợp ai đó đọc mã của bạn. Dù sao tên của biến nên càng rõ ràng càng tốt để biết nó chứa những đối tượng nào. – htafoya

-2

Các lớp Collections cung cấp bởi Apple và GNUStep khuôn khổ là bán chung ở chỗ chúng cho rằng họ là những đối tượng nhất định, một số có thể phân loại và một số trả lời tin nhắn nào đó. Đối với các nguyên thủy như float, ints, vv, tất cả cấu trúc mảng C là nguyên vẹn và có thể được sử dụng, và có các đối tượng bao bọc đặc biệt cho chúng để sử dụng trong các lớp thu chung (ví dụ NSNumber). Ngoài ra, một lớp Bộ sưu tập có thể được phân loại phụ (hoặc được sửa đổi cụ thể qua các danh mục) để chấp nhận các đối tượng thuộc bất kỳ loại nào, nhưng bạn phải tự viết tất cả mã xử lý loại. Tin nhắn có thể được gửi đến bất kỳ đối tượng nào nhưng phải trả về giá trị rỗng nếu không phù hợp với đối tượng hoặc tin nhắn sẽ được chuyển tiếp đến một đối tượng thích hợp. Các lỗi kiểu đúng sẽ bị bắt tại thời gian biên dịch, không phải lúc chạy. Vào thời gian chạy, chúng phải được xử lý hoặc bỏ qua. Cuối cùng, Objc cung cấp các cơ sở phản xạ thời gian chạy để xử lý các trường hợp khó khăn và phản hồi tin nhắn, loại cụ thể và dịch vụ có thể được kiểm tra trên một đối tượng trước khi gửi tin nhắn hoặc đưa vào bộ sưu tập không phù hợp. Hãy coi chừng rằng các thư viện và khung công tác khác nhau áp dụng các quy ước khác nhau về cách đối tượng của chúng hoạt động khi gửi thư mà chúng không có phản hồi mã, vì vậy RTFM. Ngoài các chương trình đồ chơi và các bản dựng lỗi, hầu hết các chương trình không cần phải sụp đổ trừ khi chúng thực sự trục trặc và cố ghi dữ liệu xấu vào bộ nhớ hoặc đĩa, thực hiện các hoạt động bất hợp pháp (ví dụ: chia cho số không, nhưng bạn cũng có thể nắm bắt) hoặc truy cập tài nguyên hệ thống ngoài giới hạn. Tính năng động và thời gian chạy của Objective-C cho phép mọi thứ thất bại một cách duyên dáng và phải được xây dựng trong mã của bạn. (GỢI Ý) nếu bạn đang gặp rắc rối với tính tổng quát trong các chức năng của mình, hãy thử một số tính đặc hiệu. Viết các hàm với các kiểu cụ thể và để cho thời gian chạy được chọn (đó là lý do tại sao chúng được gọi là các bộ chọn!) Thành viên-chức năng thích hợp tại thời gian chạy.

Example: 
    -(id) sort (id) obj; // too generic. catches all. 
    // better 
    -(id) sort: (EasilySortableCollection*) esc; 
    -(id) sort: (HardToSortCollection*) hsc; 
    ... 
    [Sorter sort: MyEasyColl]; 
    [Sorter sort: MyHardColl]; 
4

NS chung có thể được thực hiện bằng cách phân lớp NSArray và xác định lại tất cả các phương pháp được cung cấp với các phương thức hạn chế hơn. Ví dụ,

- (id)objectAtIndex:(NSUInteger)index 

sẽ phải được định nghĩa lại trong

@interface NSStringArray : NSArray 

như

- (NSString *)objectAtIndex:(NSUInteger)index 

cho một NSArray chứa chỉ NSStrings.

Lớp con được tạo có thể được sử dụng như là một thay thế thả và mang lại nhiều tính năng hữu ích: cảnh báo trình biên dịch, quyền truy cập thuộc tính, tạo mã tốt hơn và -hoàn toàn trong Xcode. Tất cả đều là các tính năng biên dịch-thời gian, không cần phải xác định lại việc thực hiện thực tế - các phương thức của NSArray vẫn có thể được sử dụng.

Có thể tự động hóa điều này và đun sôi xuống chỉ hai câu lệnh, điều này mang lại cho nó gần với các ngôn ngữ hỗ trợ Generics. Tôi đã tạo tự động hóa với WMGenericCollection, nơi các mẫu được cung cấp dưới dạng Macro tiền xử lý C.

Sau khi nhập tệp tiêu đề chứa macro, bạn có thể tạo một NSArray chung với hai câu lệnh: một cho giao diện và một cho việc triển khai. Bạn chỉ cần cung cấp kiểu dữ liệu bạn muốn lưu trữ và tên cho các lớp con của bạn. WMGenericCollection cung cấp các mẫu như vậy cho NSArray, NSDictionaryNSSet cũng như các đối tác có thể thay đổi của chúng.

Một ví dụ: List<int> có thể được thực hiện bởi một lớp tùy chỉnh được gọi NumberArray, được tạo ra với các tuyên bố sau đây:

WMGENERICARRAY_INTERFACE(NSNumber *, // type of the value class 
         // generated class names 
         NumberArray, MutableNumberArray) 

Khi bạn đã tạo NumberArray, bạn có thể sử dụng nó ở khắp mọi nơi trong dự án của bạn. Nó thiếu cú ​​pháp của <int>, nhưng bạn có thể chọn lược đồ đặt tên của riêng bạn để gắn nhãn chúng dưới dạng các lớp làm mẫu.

+0

lưu ý rằng cùng tồn tại trong CoreLib: https://github.com/core-code/CoreLib/blob/master/CoreLib/CoreLib.h#L105 – user1259710

2

Bây giờ giấc mơ trở thành hiện thực - có Generics trong Objective- C kể từ hôm nay (cảm ơn, WWDC). Nó không phải là một trò đùa - trên official page của Swift:

New syntax features let you write more expressive code while improving consistency across the language. The SDKs have employed new Objective-C features such as generics and nullability annotation to make Swift code even cleaner and safer. Here is just a sampling of Swift 2.0 enhancements.

Và hình ảnh đó chứng minh điều này: Objective-C generics

5

This was released in Xcode 7

Lưu ý rằng trong mã C Mục tiêu, nó chỉ là một compile- (cuối cùng!) xem thời gian; sẽ không có lỗi thời gian chạy chỉ để đưa loại sai vào một tập hợp hoặc gán cho một thuộc tính đã gõ.

Declare:

@interface FooClass <T> : NSObject 
@property (nonatomic) T prop; 
@end 

Sử dụng:

FooClass<NSString *> *foo = [[FooClass alloc] init]; 
NSArray<FooClass<NSString *> *> *fooAry = [NSArray array]; 

Hãy cẩn thận về những * s.

+0

Đẹp. :) Cảm ơn các cập nhật. – Qix

2

Chỉ muốn nhảy vào đây. Tôi đã viết một bài đăng trên blog over here về Generics.

Điều tôi muốn đóng góp là Generics có thể được thêm vào bất kỳ lớp học nào, không chỉ các lớp thu thập như Apple cho biết.

Tôi đã thêm thành công vào một loạt các lớp khi chúng hoạt động giống hệt như bộ sưu tập của Apple. I E. biên dịch kiểm tra thời gian, hoàn thành mã, cho phép loại bỏ phôi, v.v.

Tận hưởng.

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