1

Xcode 7 introduced generics. It allows for hint, like:

And allows for warning, like:

But it fails for many things, with no warning at all:

NSMutableArray<NSNumber *> *array;
array = [NSMutableArray<NSNumber *> arrayWithObjects:@0, @"", nil];
NSLog(@"%@", [array[1] class]);// __NSCFConstantString
array = [NSMutableArray<NSNumber *> arrayWithContentsOfURL:[NSURL URLWithString:@"http://ios.eezytutorials.com/sample-files/sample-array-plist.plist"]];
NSLog(@"%@", [array[0] class]); // __NSCFString

So typing the generic creation is kind of unreliable.

Question: should we use the unreliable syntax?

NSMutableArray<NSString *> *array;
array = [NSMutableArray<NSString *> array];
array = [NSMutableArray<NSString *> arrayWithArray:array];
array = [NSMutableArray<NSString *> arrayWithObjects:@"", @"", nil];
array = [NSMutableArray<NSString *> arrayWithContentsOfURL:url];

Or the simplified syntax?

NSMutableArray<NSString *> *array;
array = [NSMutableArray array];
array = [NSMutableArray arrayWithArray:array];
array = [NSMutableArray arrayWithObjects:@"", @"", nil];
array = [NSMutableArray arrayWithContentsOfURL:url];

I'm also concerned by the fact it is obviously not possible to give any compile-time guaranty of the return value of arrayWithContentsOfURL and arrayWithContentsOfFile. So will generics be always useless for those?

Cœur
  • 37,241
  • 25
  • 195
  • 267
  • Why you want to loose the Dynamism of Objective-C if you are not sure the array will hold what kind of objects? – Anoop Vaidya Jan 08 '16 at 12:23
  • 1
    This answer can maybe help you : http://stackoverflow.com/a/5198040/3222713 – Pipiks Jan 08 '16 at 12:26
  • Thanks @Pipiks, I understand it as to use the lightweight typing. – Cœur Jan 08 '16 at 12:32
  • @AnoopVaidya Not knowing the type of thing an array will hold is not the general case and likely poor design. – zaph Jan 08 '16 at 12:40
  • @zaph: So suddenly from 2015 all the old code became poor!!! I meant we store different types of objects in an array, JSON returned NSDictionary, server error converted to NSError, our own error description as NSString... – Anoop Vaidya Jan 08 '16 at 12:43
  • In general "yes" that is poor design. – zaph Jan 08 '16 at 12:53
  • 1
    for ``, the items should be `@"..."`, for `` the items should be `@(...)`, that is why you get the warning, you add `NSNumber` in spite of you defined the array's item type is `NSString`. – holex Jan 08 '16 at 17:00
  • @holex, that's obvious, and that's how I artificially made this warning to demonstrate the use of the syntax. – Cœur Jan 09 '16 at 11:32

3 Answers3

4

Yes, one should use generics to provide further type specification information both to the compiler and for the developers.

Providing the type of object that an array will contain in the declaration is at a minimum a documentation gain and error checking is an additional benefit.

There is also the hope that type checking will improve in future versions of the compiler.

But in general mixing types in an array by accident has not been a problem, such mistakes tend to show confusion on the part of the developer. If I have an array of Motorcycle objects and add a Juggler object I either have a major design mismatch error or major confusion.

The best defense is good class and variable naming, not array but motorcycleList and jugglerList. Then if I have motorcycleList addObject:juggler it is quickly apparent there is an error right when the statement is being written.

This addition to the ObjC language is just a step in a progression of explicit specification:

id x = [NSMutableArray new];
NSMutableArray *list = [NSMutableArray new];
NSArray<NSString *> *stringList = [NSMutableArray new];
zaph
  • 111,848
  • 21
  • 189
  • 228
  • 2
    The documentation bit is so important that even if there was no checking, it still would be worth to use generics. – Sulthan Jan 08 '16 at 12:48
  • One implementation comes to my mind, assume I have an `NSView` and I loop through all the subViews, it may contain `NSButton`, `NSTextField`, `NSBox`, `NSView` etc. So in future how will be do this? Is this *really* a design flaw? – Anoop Vaidya Jan 08 '16 at 12:53
  • That is a poor example because they are all subclasses of `NSView`, it is an array of `NSView` objects. Throwing in an `NSNumber` would be inappropriate. – zaph Jan 08 '16 at 12:57
  • Sorry, but I like to know, how exactly it works. As you mentioned all are subclasses of `NSView`, then every object would be subclass of `NSObject` assuming `NSProxy` is not used. – Anoop Vaidya Jan 08 '16 at 12:59
  • 1
    Reductio ad absurdum? – zaph Jan 08 '16 at 13:05
  • Thanks, that helped me and will help others as well. – Anoop Vaidya Jan 08 '16 at 13:09
  • 1
    I've seen a lot of mistakes from people putting the wrong things in arrays. The most common in my experience is when there are models and view models (which is particularly bad because they often have many of the same methods so can often "work"). A particularly notorious example for one team was a system with both `Person` and `PersonRecord` classes. Someone would make arrays of `PersonRecords` and call the variable `people` and later maintainers would make exactly the mistake you'd imagine. Careful naming is so important, but generics would have saved us a lot of headaches. – Rob Napier Jan 08 '16 at 13:46
  • @AnoopVaidya In the distant past in Objective-C `id` was used extensively as a type. The we started using specific type names and that vastly improved the code readability and error checking as well as reducing the need for casting. Now comes the ability to specify the types of array contents. If I create an array `NSArray *viewArray;` it is clear that I can send any `NSView` message to any object in the array without further type checking; that is a positive addition. It also tells me I can/should not add an `NSError *` or other object that are not at least subtypes of `NSView`. – zaph Jan 08 '16 at 14:55
4

You're right, generics in Obj-C are imperfect, but:

  • Some type checking is better than none
  • Makes the code more self-documenting
  • Future versions of Xcode could improve the type inference
  • It already smooths integration with Swift -- something that it only going to get more important over time

Cons:

  • Doesn't spot 100% of errors -- but does anything?
  • Adds some extra cruft to your code

In summary: a slight improvement right now but makes your code more future-proof.

Stephen Darlington
  • 51,577
  • 12
  • 107
  • 152
0

Because it does trigger the warning described in the question, using the constructor with specified generic type ([NSMutableArray<NSString *> ...];) is a better approach.

But we can assume that generics in Objective-C were introduced for transitioning to Swift, and in that regard, using Swift directly is even better.

Cœur
  • 37,241
  • 25
  • 195
  • 267