Adopting Objective-C Generics
miqu.me
miqu.me
So why was this added to the language? I have no idea. I guess it's just some kind of CS dogma -- the same that guided the design of Swift.
For Swift's benefit. The types come across the ObjC-Swift bridge. It's a big pain to deal with arrays typed as [AnyObject] in Swift, so this helps.
I have, however, spent untold cumulative hours looking up the documentation of a call that took or returned NSArray* just to see what kind of objects it expected or provided in that array.
Cocoa is filled with methods like:
- (NSArray *)items;
What does it return? An array! What's in the array? Items! What class represents items? Who knows!Sometimes it's obvious, sometimes it's not. Sometimes you have to look up the documentation and see. Sometimes, if you're really unlucky, you have to just print the thing out at runtime and see. It's almost always a pain in the ass.
Good Objective-C code will add a comment to the above to the effect of, // This returns an array of MyFooItem instances. This allows the programmer to quickly see what they can expect. Generics just formalizes this and puts it in a form that the compiler can understand. What's so bad about that?
One of the reasons I've always liked Obj-C is that it is (was) such a thin layer over C, and the amount of new syntax was minimal. Devising new ways to litter code with <> brackets smells suspiciously like C++.
This addition is so distant from C++ that New Horizons hasn't even reached it yet.
Objective-C classes have Swift interfaces automatically generated so you can call them from Swift code. Up until now this has been pretty shitty, since Swift is overall a stricter language than Obj-C. e.g., calls in Swift can enforce a return value to be non-nil, but Obj-C cannot, therefore all Obj-C-Swift interfaces leaves these values in an unknown nullable state even if the code itself makes guarantees on nilness.
The "nice" bits being added allow proper generation of Swift interfaces so that Swift code calling Obj-C code doesn't lose type safety (or nil-safety).
With static checks, you can guarantee this absolutely never happens. Whether Objective-C's checks reach are as exhaustive as they could be, I don't know, but I think this seems to be one case where the future is turning out to be actual genuine progress rather than merely the inevitable outcome of the passage of time.
Most larger projects (including most of Apple's own software) have a C++ core underlying an Objective-C GUI layer. This split is actually quite useful in practice. C++ language features are useful for data modelling, whereas Obj-C's dynamism is useful for building GUIs.
Hence I feel that Obj-C doesn't automatically benefit from new C++-like syntax, because real-world code is already using C++ for the sections of code where it matters.
The interactions between C, C++ and Obj-C are already pretty complex. Adding new syntax into Obj-C may only muddy the waters further. I'm not sure if it's worth adding stuff to Obj-C at this point in its history.
Of course since you are programming since 14 years, maybe you never switched to property notation? In that case you'll at least profit from better autocomplete.
But I do use property notation, if only because not doing so would probably drive my co-workers crazy. It's more important to write code that is readable in context; nobody cares about my personal syntactic tastes. (That's of course why I like to whine about them on HN.)
This looks like type erasure (checking the types in the compiler but treating everything as id under the hood, similar to what Java does), whereas swift, if I'm not mistaken, uses reification for generics (creating separate copies of each class/function for each parameter type used in the application).
Does that mean that the compiler is doing erasure for generics coming in from Objective C, inserting runtime casts where needed, but then doing full reification on swift code?
Seems like that would have to be how it works, no?
let a = A()
a.someMethod()
let b = B()
b.someMethod()
If A and B are Swift types, then the code to invoke a.someMethod() might be substantially different from the code for b.someMethod(). There's struct versus class, final methods, the potential for inlining, maybe more.If A and B are both Objective-C classes, however, then the code for the two invocations is identical. Both will compile down to a call to objc_msgSend, passing the object and the "someMethod" selector to it as parameters.
So yes, it looks like the types get erased on the ObjC end of things, but I don't think the Swift end of things has to care very much. It can emit code without any runtime casts or any adjustment for the actual generic type.
The runtime types of Obj-C objects do not matter, as long as they can respond to the messages sent to them. In fact, you can cast any object to any class and gets no runtime errors, as long as the message you send is recognized. So reification is really not something useful in Obj-C.