143

I'm new to Mac/iPhone programming and Objective-C. In C# and Java we have "generics", collection classes whose members can only be of the type declared. For example, in C#

Dictionary<int, MyCustomObject>

can only contain keys that are integers and values that are of type MyCustomObject. Does a similar mechanism exist in Objective-C?

Quinn Taylor
  • 44,553
  • 16
  • 113
  • 131
Rich
  • 36,270
  • 31
  • 115
  • 154
  • Just starting to learn about ObjC myself. Perhaps you can use ObjC++ to do the heavy lifting? – Toybuilder May 11 '09 at 15:32
  • 2
    ObjC++ isn't really a language... just more of a way to reference ObjC's ability to handle C++ inline just the same as it would handle C. You shouldn't do this unless you have to, though (such as if you need to use a third-party library that was written in C++). – Marc W May 11 '09 at 15:45
  • Pretty much an exact duplicate of http://stackoverflow.com/questions/649483/is-there-any-way-to-enforce-typing-on-nsarray-nsmutablearray-etc – Barry Wark May 11 '09 at 15:45
  • @ Mark W - "shouldn't do this" why not? I've used ObjC++ and it works great. I can do #import and @property std::map myDict; I can use the full Cocoa api AND have strongly-typed collections. I don't see any down-side. – John Henckel Apr 29 '14 at 16:02
  • You may be interested in answers to this question: [Is there any way to enforce typing on NSArray, NSMutableArray, etc.?](http://stackoverflow.com/questions/649483/is-there-any-way-to-enforce-typing-on-nsarray-nsmutablearray-etc). Arguments are given why it is not common practice in Objective-C/Cocoa. – mouviciel May 11 '09 at 15:33
  • Apple just introduced generics along with Xcode 7. – Pedro Mancheno Jun 09 '15 at 15:24

11 Answers11

215

In Xcode 7, Apple has introduced 'Lightweight Generics' to Objective-C. In Objective-C, they will generate compiler warnings if there is a type mismatch.

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 *'

And in Swift code, they will produce a compiler error:

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

Lightweight Generics are intended to be used with NSArray, NSDictionary and NSSet, but you can also add them to your own classes:

@interface GenericsTest<__covariant T> : NSObject

-(void)genericMethod:(T)object;

@end

@implementation GenericsTest

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

@end

Objective-C will behave like it did before with compiler warnings.

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

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

but Swift will ignore the Generic information completely. (No longer true in 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

Connor Pearson
  • 63,902
  • 28
  • 145
  • 142
  • Since I have question about generics and types returned in methods I asked my question in different thread, to keep everything clear: http://stackoverflow.com/questions/30828076/objective-c-generics-not-working-for-methods – lvp Jun 14 '15 at 10:02
  • 2
    @rizzes. Yes, it was just introduced. – Connor Pearson Jul 29 '15 at 12:26
  • One caveat here is that Swift doesn't _entirely_ ignore the type annotations in your generic ObjC class. If you specify constraints, e.g. `MyClass >`, your Swift code will assume values are the type of your constraint, which gives you _something_ to work with. However, specialized subclasses of `MyClass` would have their specialized types ignored (be seen effectively the same as a generic `MyClass`). See https://github.com/bgerstle/LightweightGenericsExample – Brian Gerstle Oct 01 '15 at 14:32
  • So does this compile for 10.10, 10.9 and earlier operating systems? – p0lAris Oct 22 '15 at 00:21
  • It should as long as you set your deployment target to support them – Connor Pearson Oct 22 '15 at 00:32
  • Swift 3 should import the lightweight generics: https://github.com/apple/swift-evolution/blob/master/proposals/0057-importing-objc-generics.md – Jeff Kelley May 18 '16 at 04:00
91

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


No, there are no generics in Objective-C unless you want to use C++ templates in your own custom collection classes (which I strongly discourage).

Objective-C has dynamic typing as a feature, which means that the runtime doesn't care about the type of an object since all objects can receive messages. When you add an object to a built-in collection, they are just treated as if they were type id. But don't worry, just send messages to those objects like normal; it will work fine (unless of course one or more of the objects in the collection don't respond to the message you are sending).

Generics are needed in languages such as Java and C# because they are strong, statically typed languages. Totally different ballgame than Objective-C's dynamic typing feature.

Aaron Brager
  • 65,323
  • 19
  • 161
  • 287
Marc W
  • 19,083
  • 4
  • 59
  • 71
  • 88
    I disagree to "don't worry, just send messages to those objects". If you put the wrong type of objects into the collection, which do not respond to these messages, this will yield runtime errors. Using generics in other languages avoids this problem with compile time checks. – henning77 Jan 04 '12 at 16:24
  • 8
    @henning77 Yes, but Objective-C is a more dynamic language than these languages. If you want strong type-safety, use those languages. – Raffi Khatchadourian Jan 16 '12 at 09:11
  • 36
    I also disagree to the don't worry philosophy - for example if you pull the first item out of an NSArray and cast it to an NSNumber but that item was really an NSString, you are screwed... – jjxtra Jan 19 '12 at 20:27
  • You all may be reading into that wording a bit much, but I see how you could come to the conclusion that I was implying that no errors could occur (which is not what I meant to imply). – Marc W Jan 20 '12 at 16:37
  • 2
    @Marc: then consider changing the wording? – bacar Jan 29 '12 at 15:10
  • @PsychoDad I agree with you, in fact is always a good practice to check with istanceof what object you're using, in Java is simpler but you can declare a collection of type Object if you need. – AR89 Oct 15 '12 at 12:58
  • 13
    @RaffiKhatchadourian -- not much choice if you're writing an iOS app. If it was simple to write one with Java, and get all the benefits of writing a native app, believe me: I would. – ericsoco Jan 28 '13 at 04:52
  • 11
    The biggest complain I have about this isn't to do with dynamic languages vs. compile time checking, but simple developer communication. I can't just look at a property declaration and know what type of objects it is going to return unless it's documented somewhere. – devios1 Mar 14 '13 at 00:39
  • 1
    @chaiguy this *is* a matter of dynamic vs static IMO – funkybro Apr 02 '13 at 13:59
  • 2
    @ericsoco perhaps Xamarin might be a good option if you don't mind spending on it; C# IMHO has many advantages over Java and few disadvantages. Compared to Objective-C... well, it compares "somewhat favourably" when it comes to such features. – PeterT Jun 08 '13 at 21:13
  • Static type checks would be good in this case. In any case a good way to soften this problem a bit is by using assertions to make sure the object is of the type you want it to be - or fail otherwise. I use something similar to `assert_class(object, [NSString class])` in my code after retrieving the object. Documenting the type of objects the array holds is important too. – diegoreymendez Aug 14 '13 at 19:40
  • Generics aren't needed in Java either. They're there because of C++ lust. – Hot Licks Aug 28 '14 at 16:20
11

No, but to make it clearer you can comment it with the type of object you want to store, I've seen this done a few times when you need to write something in Java 1.4 nowadays) e.g.:

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

or

NSDictionary* /*<TypeA, TypeB>*/ dictionaryName = ...
Mark Rhodes
  • 129
  • 1
  • 2
  • I guess this is a good way to have it documented, in case someone else reads your code. Anyway the variable's name should be as clear as possible to know what objects it contains. – htafoya Dec 16 '12 at 18:27
7

This was released in Xcode 7 (finally!)

Note that in Objective C code, it's just a compile-time check; there will be no run-time error just for putting the wrong type into a collection or assigning to a typed property.

Declare:

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

Use:

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

Be careful about those *s.

Kevin
  • 53,822
  • 15
  • 101
  • 132
6

Apple has added generics to ObjC in XCode 7:

@property NSArray<NSDate *>* dates;
- (NSArray<NSDate *> *)datesBeforeDate:(NSDate *)date;
- (void)addDatesParsedFromTimestamps:(NSArray<NSString *> *)timestamps;

see here: https://developer.apple.com/library/prerelease/mac/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6-ID61

user1259710
  • 868
  • 2
  • 9
  • 12
6

There are no generics in Objective-C.

From the Docs

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

Matthew Vines
  • 27,253
  • 7
  • 76
  • 97
4

Generic NSArrays can be realized by subclassing NSArray, and redefining all provided methods with more restrictive ones. For example,

- (id)objectAtIndex:(NSUInteger)index

would have to be redefined in

@interface NSStringArray : NSArray

as

- (NSString *)objectAtIndex:(NSUInteger)index

for an NSArray to contain only NSStrings.

The created subclass can be used as a drop-in replacement and brings many useful features: compiler warnings, property access, better code creation and -completion in Xcode. All these are compile-time features, there is no need to redefine the actual implementation - NSArray's methods can still be used.

It's possible to automate this and boil it down to only two statements, which brings it close to languages that support generics. I've created an automation with WMGenericCollection, where templates are provided as C Preprocessor Macros.

After importing the header file containing the macro, you can create a generic NSArray with two statements: one for the interface and one for the implementation. You only need to provide the data type you want to store and names for your subclasses. WMGenericCollection provides such templates for NSArray, NSDictionary and NSSet, as well as their mutable counterparts.

An example: List<int> could be realized by a custom class called NumberArray, which is created with the following statement:

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

Once you've created NumberArray, you can use it everywhere in your project. It lacks the syntax of <int>, but you can choose your own naming scheme to label these as classes as templates.

w-m
  • 10,772
  • 1
  • 42
  • 49
2

Take a look at:

https://github.com/tomersh/Objective-C-Generics

It appears to be a sort of poor-man's generics, by repurposing the protocol checking mechanism.

David Jeske
  • 2,306
  • 24
  • 29
2

Now dreams come true - there are Generics in Objective-C since today (thanks, WWDC). It's not a joke - on official page of 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.

And image that proofs this:Objective-C generics

htzfun
  • 1,231
  • 10
  • 41
1

Just want to jump in here. I've written a blog post over here about Generics.

The thing I want to contribute is that Generics can be added to any class, not just the collection classes as Apple indicates.

I've successfully added then to a variety of classes as they work exactly the same as Apple's collections do. ie. compile time checking, code completion, enabling the removal of casts, etc.

Enjoy.

drekka
  • 20,957
  • 14
  • 79
  • 135
-2

The Collections classes provided by Apple and GNUStep frameworks are semi-generic in that they assume that they are given objects, some that are sortable and some that respond to certain messages. For primitives like floats, ints, etc, all the C arrays structure is intact and can be used, and there are special wrapper objects for them for use in the general collection classes (eg NSNumber). In addition, a Collection class may be sub-classed (or specifically modified via categories) to accept objects of any type, but you have to write all the type-handling code yourself. Messages may be sent to any object but should return null if it is inappropriate for the object, or the message should be forwarded to an appropriate object. True type errors should be caught at compile-time, not at run-time. At run-time they should be handled or ignored. Finally, Objc provides run-time reflection facilities to handle tricky cases and message response, specific type, and services can be checked on an object before it is sent a message or put into an inappropriate collection. Beware that disparate libraries and frameworks adopt different conventions as to how their objects behave when sent messages they do not have code responses for, so RTFM. Other than toy programs and debugging builds, most programs should not have to crash unless they really screw up and try to write bad data to memory or disk, perform illegal operations (eg divide by zero, but you can catch that too), or access off-limits system resources. The dynamism and run-time of Objective-C allows for things to fail gracefully and should be built in to your code. (HINT) if yo are having trouble with genericity in your functions, try some specificity. Write the functions over with specific types and let the runtime select (thats why they are called selectors!) the appropriate member-function at run-time.

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