// *** before: ***
// a collection held id, so what was inside it could only be guessed from
// the name of the variable
@interface Library : NSObject
@property (nonatomic, copy) NSArray *books; // of what?
@property (nonatomic, copy) NSDictionary *index; // key and value of what?
@end
NSArray *books = library.books;
NSString *title = [books objectAtIndex:0]; // no warning at all
NSUInteger length = title.length; // crashes: it was a Book
// *** in version 2015: ***
// the element type is written in the declaration
@interface Library : NSObject
@property (nonatomic, copy) NSArray<Book *> *books;
@property (nonatomic, copy) NSDictionary<NSString *, Book *> *index;
@property (nonatomic, copy) NSSet<NSString *> *tags;
@end
NSArray<Book *> *books = library.books;
Book *book = books[0]; // the type is known
NSString *title = book.title;
NSString *wrong = books[0];
// warning: incompatible pointer types initializing NSString * with Book *
Book *found = library.index[@"Moby-Dick"];
for (Book *each in library.books) // the loop variable is typed too
NSLog(@"%@", each.title);
// ⚠️ the generics are "lightweight": they exist for the compiler only. The
// runtime has no idea about them, nothing is checked when the array is
// filled from untyped code, and NSArray<Book *> and NSArray are one and
// the same class