I've been programming in Obj-C for 14 years. When I started, I was worried about adding the wrong kind of object into an NSArray. But turns out that this practically never happens. I can't remember the last time I had a bug that would have been prevented by generics.
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.
I've never had an error where I added the wrong kind of object to an array.
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?
There's nothing wrong with that, it's nice. But that's all it is IMHO -- just nice. So I'm not sure why they're bothering to add it to Objective-C now.
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++.
They're adding it to Objective-C now to make it interop better with Swift.
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).
Nothing wrong with a nice bit of CS dogma! You're welcome to ignore what those fancy pants doctors with their clever titles tell you to do. After all, they just read a few books, and books could say anything. But for my part, I think this is a good move, since I don't think "practically never" is a very high bar ;)
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.
You have to understand that Obj-C is sort of a single-purpose language: it's primarily used for user interface code.
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.
these generics finally let you use property notation with arrays; you can now do something like 'names[i].length' or 'self.tableView.tableColumns.firstObject.identifier'.
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.
Well, you got me there -- I honestly feel that property notation was not a very good addition to Obj-C because it looks like C struct access yet is incompatible with it. It's supremely annoying that I can't write "view.frame.origin.x += 10"...
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.)
I heavily lean on the type system less for "no bugs" (though frankly I am surprised that you never accidentally confuse a list of URLs stored as NSString with a list of NSURLs ;P) and more for helping me rapidly refactor my codebase: I can make some signature changes and then let the compiler walk me through the required remaining changes.
Curious as to how this actually works under the hood.
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?
There's not much reification to be done when dealing with ObjC objects and calling ObjC methods. For example:
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.
> Does that mean that the compiler is doing erasure for generics coming in from Objective C, inserting runtime casts where needed
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.
18 comments
[ 2.4 ms ] story [ 27.4 ms ] threadSo 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:
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?
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.