Có các bộ sưu tập được gõ mạnh trong Objective-C không?


140

Tôi chưa quen với lập trình Mac / iPhone và Objective-C. Trong C # và Java, chúng ta có "generic", các lớp tập hợp mà các thành viên chỉ có thể thuộc kiểu khai báo. Ví dụ: trong C #

Dictionary<int, MyCustomObject>

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


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

Bạn có thể quan tâm đến câu trả lời cho câu hỏi này: Có cách nào để thực thi gõ trên NSArray, NSMutableArray, v.v. không? . Các lập luận được đưa ra tại sao nó không phải là thông lệ phổ biến trong Objective-C / Ca cao.
mouviciel

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ý nội tuyến C ++ giống như nó sẽ xử lý C. Tuy nhiên, bạn không nên làm điều này trừ khi bạn phải làm vậy (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 ++).
Marc W

Khá nhiều bản sao chính xác của stackoverflow.com/questions/649483/ Khăn
Barry Wark

@ Mark W - "không nên làm điều này" tại sao không? Tôi đã sử dụng ObjC ++ và nó hoạt động rất tốt. Tôi có thể làm #import <map> và @property std :: map <int, NSString *> myDict; Tôi có thể sử dụng api ca cao đầy đủ VÀ có các bộ sưu tập được gõ mạnh. Tôi không thấy bất kỳ mặt trái nào.
John Henckel

Câu trả lời:


211

Trong Xcode 7, Apple đã giới thiệu 'Generics nhẹ' cho Objective-C. Trong Objective-C, chúng sẽ tạo cảnh báo trình biên dịch nếu có kiểu không khớ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 mã Swift, chúng sẽ tạo ra lỗi trình biên dịch:

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

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

@interface GenericsTest<__covariant T> : NSObject

-(void)genericMethod:(T)object;

@end

@implementation GenericsTest

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

@end

Objective-C sẽ hoạt động giống như trước đây với các cảnh báo của 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'

Ngoài các lớp bộ sưu tập Foundation, các tướng nhẹ của Objective-C bị Swift bỏ qua. Bất kỳ loại nào khác sử dụng thuốc generic nhẹ đều được nhập vào Swift như thể chúng không được tham số hóa.

Tương tác với API Objective-C


Vì tôi có câu hỏi về thuốc generic và loại được trả về trong các phương thức, tôi đã hỏi câu hỏi của mình theo các luồng khác nhau, để giữ mọi thứ rõ ràng: stackoverflow.com/questions/30828076/
Lỗi

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

Một lưu ý ở đây là Swift không hoàn toàn bỏ qua các chú thích loại trong lớp ObjC chung của bạn. Nếu bạn chỉ định các ràng buộc, ví dụ MyClass <Foo: id<Bar>>, mã Swift của bạn sẽ cho rằng các giá trị là loại ràng buộc của bạn, nó cung cấp cho bạn một cái gì đó để làm việc. Tuy nhiên, các lớp con chuyên biệt MyClasssẽ bị bỏ qua các loại chuyên biệt của chúng (được xem một cách hiệu quả giống như một loại chung MyClass). Xem github.com/bgerstle/Light weightGenericsExample
Brian Gerstle

Vì vậy, điều này có biên dịch cho các hệ điều hành 10.10, 10.9 và trước đó không?
p0lAris

Nó sẽ miễn là bạn đặt mục tiêu triển khai của mình để hỗ trợ họ
Connor

91

Câu trả lời này đã lỗi thời nhưng vẫn còn giá trị lịch sử. Kể từ Xcode 7, câu trả lời của Connor từ ngày 8 tháng 6 năm15 chính xác hơn.


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

Objective-C có kiểu gõ động như một tính năng, điều đó 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 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 xử lý như thể chúng là kiểu id. Nhưng đừng lo lắng, chỉ cần gửi tin nhắn đến những đối tượng như bình thường; nó sẽ hoạt động tốt (tất nhiên trừ khi một hoặc nhiều đối tượng trong bộ sưu tập không trả lời tin nhắn bạn đang gửi) .

Generics là cần thiết trong các ngôn ngữ như Java và C # vì chúng là các ngôn ngữ được gõ mạnh, tĩnh. Trò chơi bóng hoàn toàn khác so với tính năng gõ động của Objective-C.


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

8
@ henning77 Có, nhưng Objective-C là 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, hãy sử dụng các ngôn ngữ đó.
Raffi Khatchadourian

36
Tôi cũng không đồng ý với triết lý không lo lắng - ví dụ: nếu bạn rút vật phẩm đầu tiên ra khỏi NSArray và ném nó vào NSNumber nhưng vật phẩm đó thực sự là một NSString, bạn sẽ bị
lừa

13
@RaffiKhatchadourian - không có nhiều sự lựa chọn nếu bạn đang viết một ứng dụng iOS. Nếu việc viết một cái bằng Java thật đơn giản và nhận được tất cả những lợi ích của việc viết một ứng dụng gốc, hãy tin tôi: Tôi sẽ làm thế.
ericsoco

11
Khiếu nại lớn nhất tôi có về điều này không liên quan đến ngôn ngữ động so với kiểm tra thời gian biên dịch, nhưng giao tiếp nhà phát triển đơn giản. Tôi không thể chỉ nhìn vào một tuyên bố tài sản và biết loại đối tượng nào sẽ trả lại trừ khi nó được ghi lại ở đâu đó.
devios1

11

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

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

hoặc là

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

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

6

Không có khái quát trong Mục tiêu-C.

Từ tài liệu

Mảng được sắp xếp các bộ sưu tập của các đối tượng. Ca cao cung cấp một số lớp mảng, NSArray, NSMutableArray (một lớp con của NSArray) và NSPulumArray.


Liên kết đến tài liệu trong câu trả lời đã chết - "Xin lỗi, trang đó không thể tìm thấy" .
Pang


5

Điều này đã được phát hành trong Xcode 7 (cuối cùng!)

Lưu ý rằng trong mã Mục tiêu C, nó chỉ là kiểm tra thời gian biên dịch; sẽ không có lỗi thời gian chạy chỉ vì đưa loại sai vào bộ sưu tập hoặc gán cho thuộc tính đã nhập.

Khai báo:

@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.


4

NSArrays chung có thể được nhận ra bằng cách phân lớp NSArrayvà xác định lại tất cả các phương thức đượ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 xác định lại trong

@interface NSStringArray : NSArray

như

- (NSString *)objectAtIndex:(NSUInteger)index

đối với NSArray chỉ chứa NSStrings.

Lớp con được tạo có thể được sử dụng như một sự thay thế thả xuống và mang lại nhiều tính năng hữu ích: cảnh báo trình biên dịch, truy cập thuộc tính, tạo mã và hoàn thành mã tốt hơn trong Xcode. Tất cả đều là các tính năng thời gian biên dịch, không cần xác định lại việc triển khai 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à rút gọn nó xuống chỉ còn hai câu, điều này mang nó gần với các ngôn ngữ hỗ trợ khái quát. Tôi đã tạo một tự động hóa với WMGenericCollection , trong đó 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 NSArray chung với hai câu lệnh: một cho giao diện và một cho 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 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 nhận ra bởi một lớp tùy chỉnh được gọi NumberArray, được tạo bằng câu lệnh sau:

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ó ở 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 này dưới dạng các lớp làm mẫu.


lưu ý rằng điều tương tự tồn tại trong CoreLib: github.com/core-code/CoreLib/blob/master/CoreLib/CoreLib.h#L105
user1259710


2

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

Các tính năng cú pháp mới cho phép bạn viết mã biểu cảm hơn trong khi cải thiện tính nhất quán trên ngôn ngữ. Các SDK đã sử dụng các tính năng Objective-C mới như chung chung và chú thích vô hiệu để làm cho mã Swift trở nên sạch hơn và an toàn hơn. Đây chỉ là một mẫu của các cải tiến Swift 2.0.

Và hình ảnh chứng minh điều này:Tổng quát khách quan-C


2

Chỉ muốn nhảy vào đây. Tôi đã viết một bài đăng trên blog 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 nào , không chỉ các lớp bộ sưu tập như Apple chỉ ra.

Sau đó, 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ư các 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.

Thưởng thức.


-2

Các lớp Bộ sưu tập được cung cấp bởi các khung công tác Apple và GNUStep là bán chung ở chỗ chúng cho rằng chúng là các đối tượng được cung cấp, một số có thể sắp xếp và một số phản hồi một số thông điệp nhất định. Đối với các nguyên hàm như float, ints, v.v., tất cả cấu trúc mảng C vẫn còn nguyên vẹn và có thể được sử dụng, và có các đối tượng trình bao bọc đặc biệt cho chúng để sử dụng trong các lớp bộ sưu tập chung (ví dụ NSNumber). Ngoài ra, một lớp Collection có thể được phân lớp phụ (hoặc được sửa đổi cụ thể thông 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ả cá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 sẽ trả về null 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. Lỗi loại đúng nên được bắt tại thời gian biên dịch, không phải lúc chạy. Tại thời điểm chạy chúng nên được xử lý hoặc bỏ qua. Cuối cùng, Objc cung cấp các phương tiện phản ánh 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 nó được gửi tin nhắn hoặc đưa vào bộ sưu tập không phù hợp. Coi chừng các thư viện và khung khác nhau áp dụng các quy ước khác nhau về cách các đối tượng của họ hành xử khi gửi tin nhắn mà họ không có phản hồi mã, vì vậy RTFM. Khác với các chương trình đồ chơi và các bản dựng gỡ lỗi, hầu hết các chương trình không phải gặp sự cố trừ khi chúng thực sự làm hỏng 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 0, nhưng bạn cũng có thể bắt được điều đó) 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à nên được tích hợp vào mã của bạn. (GỢI Ý) nếu bạn 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, thử một số tính cụ thể. Viết các hàm trên với các loại cụ thể và để cho bộ thực thi chọn (đó là lý do tại sao chúng được gọi là bộ chọn!) Hàm thành viên thích hợp trong 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];
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.