WWDC2004 Session 302
Transcript
Kind: captions Language: en pair of tools this is what we're gonna explore today I'd like to give you some context for this language because I feel it's important for understanding the trick the design decisions the trade-offs we made and in fact the roots of both of these language languages if you look at when C++ and Objective C were born they were both born in the mid 80s roughly speaking and C++ drew inspiration from Simula and objective-c drew inspiration from small talk and some of the terminology features and mindsets and in fact culture that surround these languages I hope to expose to give you some sense for why one language works one way another language solves a different set of problems and why the combination of them is actually pretty exciting I I also want to give you many examples because if we just talk in the abstract you're not going to get a good feel for the language the best way to learn the language is through example so the examples I'm going to be giving are from WebKit Safari there are some examples from the Celestra application that you've seen there are some examples from our developer examples so if you want to try some of these when you get home you can go and look at the code that in fact we'll be presenting here and and lastly I want to talk about some gotchas it's a very complex language and there are a few gotchas I don't think they affect too many people but it's still relevant in the context of this talk and lastly I don't have this on the slide but I'd love to interact with you folks for about 15 20 minutes so I'm hoping I could zoom through the presentation and customize it to what you guys want to know afterwards so to cut to the chase why why isn't cocoa written in C++ we wouldn't be here talking about the combined languages if in fact cocoa was written in C++ and the simple answer is it doesn't meet Coco's needs so before I explain why it doesn't meet Coco's needs I'm going to try and go through what are Coco's needs what are the language requirements want are the runtime requirements that we have for go to set the stage for understanding the language number one easy to learn less is more we want to be able to teach someone the language the Cocos written in in a week max I think we're gonna cover most of the language here fairly well and I think it'll get a feel for a good feel for objective-c in a couple of hours roughly but we want people to focus on the API so we want to take the spotlight from the language and move it to the frameworks that's one of the bigger reasons we wanted it to be easy to learn we don't want you to focus or struggle with the language we want to embrace existing code this has been part of the mindset for many years that is this isn't Java where we're trying to basically give you a clean new world that you have to conform to in in a homogeneous way we want you to be able to bring over your C code your C++ code and leverage it in the context of this very dynamic environment and lastly and most importantly we want a dynamic object model the dynamic object model is what enables many of the cool features that you see in these presentations whether it be cocoa bindings or Automator many of these very cool features are because we have dynamic plumbing underneath the covers that actually allows us to do some very very interesting things and if you don't write frameworks for a living you don't sometimes think about it so if you're an application programmer I'm hoping that if you haven't thought about some of these features we're going to put them in a context that's going to make them much more interesting to you so it's very it's very important that we serve the frameworks and the interactive tools one of the runtime requirements as most of you know in the mid-80s we weren't dealing with machines and memory models that were that were vast as vast as today for instance objective-c ran fairly well in two megabyte on two megabyte workstations and today as you know most workstation chip with 256 megabytes 512 whatever quite a bit more than it was designed for so it's very clear that objective-c having born in that age scales nicely to today's machines it's very critical that when we ship new features that we don't break your code okay now if you look at how many releases of cocoa we've made and lots of frameworks we've been very successful in fact I think we were the only operating system that I know of that is fairly object-oriented and hasn't in fact broken people with some regularity so everything's shared and we try not to break people we have a sea-based API and this again doesn't really affect you directly but indirectly that is we feel strongly that objective-c has to interact with other languages so for instance the first two up there cocoa Java is a bridge that Apple supports Apple script studio is another product that allows you from Apple script to do cocoa programming and then there's a whole bunch of third-party scripting languages Python for objective-c Ruby cocoa Perl and in fact c-sharp is is being added to the list of languages that are going to be available to interact with cocoa and bind with Objective C so we're really excited to see third parties in fact integrate with this runtime which we consider fairly flexible so to wrap it up it's designed to serve the dynamic frameworks and tools and it enables you to integrate your C code with cocoa but what this talk is about is Objective C plus plus which is the same exact design point okay it just enables you to integrate C++ code so it's it's it's not as if the object modules are there to compete they're there to work together so now let's get back to why C++ doesn't meet Coco's needs it's a strongly type static object model this is what one of the properties that makes it hard to evolve systems over time there's no runtime support for dynamic loading a reflection again many features that we provide in our tools would not be possible with the standard C++ definition as many of you use it today it is possible to expand the definition but we've used C++ as a standard language which we try not to extend and run time model is fragile and compiler dependent this is a feature which is hurt C++ significantly one of the reasons Java is is so popular today is because it doesn't suffer from this ok shared libraries are critical for deploying code and it contributes to this culture of code copying right where if you can't share something and not break it you want to own it you want to copy it in yourself so in fact you won't break and in fact if you have many applications using many large frameworks and they're all copied you can imagine what the memory footprint of your system would be like so objective C++ design points peaceful coexistence right we want to use C++ as the base and the parser has been modified to layer Objective C atop C++ one of the smartest things I think we did was many years ago was we implemented Objective C in the canoe compiler and that that work happened in 88 roughly and that work along with the C++ work that was going on enabled us to marry them with fairly low effort both runtime systems are unchanged ok the native ABI the semantics and the performance that you expect from C++ is what you get it's not like we're changing the way you do virtual function dispatch or any other things in C++ we are letting C++ have its own turf and Objective C have its own turf and we think that works very nicely now subclassing across object models is not supported aggregation we think works great for this now in the early days when we used to present this language people got really freaked out by that they said God you can't subclass is it really useful if you can't subclass well I think what's happened over time is people actually have learned through many iterations of designing object systems that inheritance is is often not the best tool for performing something so one things I'm going to talk about is how in Cocco uses delegation composition and aggregation as programming idioms and tools which in fact again make it a natural marriage because we don't require sub classing just by virtue of our dinette dynamism so sub classing isn't supported so why don't we unify the object models people sometimes ask who haven't carefully considered some of the implications why don't we just change C++ to work like Objective C well there are systems that have been tried over time some is one of them the system object model that Apple and IBM worked on years ago and even come to some extent by Microsoft that are actually systems designed to try and fix some of the problems with deploying C++ shared shared objects and the static object model it turns out that because they were unsuccessful we chose not to to go that path rather Mary Objective C and C++ as we've done so let's go through the benefits it's portable it's amazing how even changing initialization order of the order on which constructors are run that that breaks some of you guys out there we I work in the tools team where we're constantly looking at easier ways to port your apps and what headbutts you bump into and initialization order isn't something that the standard dictates that is it says each system can do it however it wants well in fact by the letter of the law we we work just fine however we again break some of your code because we don't do it in a specific way that let's say was born on Windows or Linux so again we want to make things portable and if initialization order is a problem if we change the way dispatch work believe me we break all of you so performance the the performance of an application is often dictated by the way it does dispatch the fine-grained model coarse-grained model of the object orientation and objective-c is just designed for more coarse-grained mid grain dynamism and not the very fine-grain stuff that people use C++ before so it would be naive to think that the same performance considerations that we apply to a dynamic more coarse-grained object apply to a fine grain once oh and and thirdly we want to be pragmatic someone said to me outside the other day how one of the beauties of coming to this conference and seeing what Apple does and seeing Steve's Keena keynote is how we we're very good at finding the sweet spot right and that's why we're able to make releases on a yearly basis and and show you guys cool stuff is because we find the sweet spot and I think objective-c is a c++ is a good example of that it's pragmatic and it works so now let's go into the code section of the talk here is hello world in four dialects you have the first header files stood I oh you guys are all familiar with that there's iostream and then there's foundation so you can see the standard printf hello world you can see the C++ version it's in all the C++ books then there's a couple a couple more statements that give you the objective-c version now that's string constant for those of you who haven't seen Objective C a string constant preceded by an @ sign is a constant string object the compiler is responsible for creating it and you can see it's initialized there and then there's the combination of them which is pretty seamless so you have standard out taking as output the C string coming from the ejecta C string object and then outputting the plus plus for the last version so you could see that that that's fairly easy to program so here's just how you compile it just to show you that the file the suffix is dot mm we don't have to get into the details of why it's called mm but it contains the mix languages and it's linking against the standard C++ library which is a static library not a shared library and it's linking against the frameworks other foundation framework so pretty easy stuff now again one detail right now we're shipping the standard C++ library as what we call an archive library a non-shared library so if you look at that executable it's actually fairly big it contains the whole standard C++ library contained within the the hello world in a future release we're going to be releasing a shared version of that now I want to go through some some you know just syntactic idioms and I also want to go through some terminology here's what a class looks like at interface so let's get the @ stuff out of the way why do we use that the reason we prefix our keywords with AD is we didn't want to break existing code it's that simple you know some people get freaked out by the @ sign but that is the reason it's we don't want to break code because code contains interface code contains end and code contains other keywords that we wanted to publish so that's why we use at and it's a single inheritance model so right there the supercut the class is my class the super class is nsobject okay for purposes of this discussion objective-c is single rooted it turns out that you can provide your own root class but that's something for an advanced session that that is a topic for another day so oh just just briefly instance variables the term instance variable is a small talk jargon or terminology in C++ if in fact some of you have just programmed in C++ you would know that as data members right so members data members instance variables map into the same concept so here we have a c type and int named x we have an NS view name Y and we have an io stream named s so you can see we have 3 different declarations here that all contain different types here are some methods their instance methods and again the term method comes from small talk instance method class method in C++ this would be member functions and well non-static member functions and member functions and here we have a set target method and a set action method now notice the colons the Collinswood separate the key what's called the keyword argument from the from the actual type and the name of the argument if you leave out the type and Objective C it means it's an ID we'll get into what I deem Eanes in a little while so here we have a set cell class that's actually a class object we'll get into that as well later and selectors selectors are a unique name for a method okay so they're like point they're like pointers to functions except they represent the name and they're unique per process and we'll talk a little bit later about that so so far we have a class interface we have instance variables we have methods here's an abstract type declaration objective-c decided to formalize the node in the notion of abstract types years ago and it's been something that's worked really well for us so an abstract type is a type where you don't specify the instance variables you don't specify the superclass you just specify what the operations that are expected of the type are so here's the NS dragging info protocol this is a set of methods which will all can will be given to the app kit and they will delegate to your object here's an abstract type implementation so you could see NS string it inherits from NS object and it conforms to three protocols so here's an example where objective-c has single inheritance of implementation but multiple inheritance of interface okay which is a very common metaphor that we use in cocoa and that works very well for us so here again we have three different interfaces or protocols and a couple methods there so here is three different object their declarations in C++ you've probably never seen anything like ID it's it's an object-oriented void star pretty much and it says here is any object now the difference is that object has metadata associated with it so in fact it's not as bad as a void star and that a void star you know nothing about it except by looking at the source code at runtime you could actually infer what the type of object is now the second statement declaration there is in NS browser star in objective-c when you type an object that's there just for documentation okay it's it tells the reader that that any browser is an NS browser it doesn't affect how the compiler will do binding so dynamic dispatch all the dynamic properties still apply even though it's been typed and the third declaration there says I have any object that conforms to the NS dragging info protocol which is what I just showed in the previous slide so that means any class that can conform to that protocol will be accepted by the compiler what's really neat about this again is you don't have to know what type of object it is now you can even provide a list there it's fairly uncommon but just as you had a list within the class declaration context you could have a list here as well another point as contrast to Java is Java integrates the abstract type namespace in with the class namespace in objective-c they're separate so you could have an NS object class and an NS object protocol and so on and so forth so they're in separate namespaces here we're going to go through message expressions this is what you see the most of when you look at an objective-c program here's the most simple one we have my view which is an object and it's sending the timer update message there's no arguments there okay the second its message expression there well is is any browser sent the set delegate object message that takes one argument notice the colon separates the name from the actual argument in this case odd and the third one a common idiom is where the receiver is derived from a message here self is I think the first time we've seen self self is equivalent to this in C++ and it's sending the flush window message so now let's look at some hybrid message expressions this first one is out of web core it's sending the message keyboard UI and you can see that it's accessing a static member function bridge for widget and in this case this is active so this is just a message expression I pulled out of web core to show you how easy it is to mix the two two languages and message expressions the second one is a bit more complicated there we have the adapter sending the set cookies for URL policy base URL message and you can see it's interspersed with arguments that are in fact member function calls okay so fairly easy stuff again a message expression is an expression so just as you call in a function and you could provide expressions to each argument of the function the same things true here that's why the nesting works so nicely there's nothing special about it okay there we go so here are some more hybrid message expressions front celestia here we have we're sending the star message to self we're then dereference saying it calling the set luminosity member function and we're passing it the float value from an objective-c message expression again you could see how natural the given play is there's nothing real really magic going on and you could really see the two languages collaborating and and working well together here so the second one we have NS number and you can see the self star and it's sending the get temperature on member function call and the third one we have simply a namespace named Astro and there's just a standard C++ function that's embedded within the namespace a stroke called light-years two parsecs and it's being passed the float value okay next thing I'd like to talk about is some dynamic idioms okay method categories are something we use pretty extensively in the application kit it allows us to augment existing classes it's something you don't see very commonly and other object-oriented languages if you look at Common Lisp or you look at some other more dynamic languages they're not called categories but they allow you to do the same thing what they allow you to do is add operations to an existing class so here's an example we're on the app kit is adding some string drawing methods to the foundation string object likewise here's an objective C++ category that Celestia adds to convert NS strings to and from the standard strings so I want to show you briefly how it works because I think many people who are new to some of the dynamic properties that objective-c has they get confused so here are three bodies of code we have the foundation where NS string lives we have a pit with a category that you just saw we have Celestia with that on that category with the two methods what happens is at runtime the runtime links things up and looks actually when it loads the shared library it says Oh a category who are you associated with and then it goes and looks up the class dynamically and it sets the method tables accordingly so that you can basically extend the class methods in a fairly seamless way here you see the NS string method table points to the string drawing method table which points to so anything in the app kit all your applications get for free if you want to have a private category as Celestia does that's a category that would be embodied with the actual executable for the application that would obviously not be shared with other executables and what what this allows you to associate methods with other objects that work on their behalf so for instance these method tables aren't a part of NS view they're not a part of the Celestia galaxy but there are methods the galaxy and the view would like to call on behalf of the string object so so why is all this interesting it's because you don't have control of who instantiates objects all the time right one of the things that frameworks are really good at is doing work on your behalf where you actually don't know what class is going to be it is going to be sent the message so we think this works really well and in fact people have used it in very interesting ways in various applications so another dynamic idiom we have the set cell class method basically classes are first-class objects in objective-c you can reason about them if you dynamically load code you can find out what classes are a part of that code and and invoke methods on them set cell class you could reason about the superclass these are all small talk idioms that we carried over into objective-c and these kind of predicate to basically ask is this class a kind of another class now notice the function there the function allows you to take a string and convert it into an object okay that's another metaphor we use quite heavily because the compiler can't always know or be told what class often it's reading those things from external files like your nib file so here our methods are first-class objects as well and here's a selector the unique code so if you want to compare selectors you need to convert them if you want to get a method signature you need a selector so in this case it's already been converted we send that message signatures contain the type so if you're writing a tool you're writing a framework and you want to know what types the the method takes the signature is really handy and here's a net selector directive a selector is the equivalent of the function in this slide the only difference is you're telling the compiler to do it so it's a little more optimal because you know the name in this case it's a lock so the compiler will get the unique code for the a lock method delegation okay this is something that we use in notifications we use it in the target action paradigm and the application kit we we do things like oh if your object responds to this elector wall send the window didn't move message the window didn't move message as part of a protocol that we publish for tracking window operations and this is this is a perfect example of code that you don't often write but is written all the time in the framework to collaborate with your objects so again it's it's an interesting case where until you see some of these idioms and metaphors and patterns you don't appreciate them and again this is this is one of the reasons when you write cocoa programs you don't find you're overwhelmed with sub-classing writing lots of different classes and in fact ending up with some very highly structured or overly structured class hierarchy it's fairly freeform it's it's it's more free-flowing so here set target set action this is code from the interface builder paradigm and this is you know and all of our controls and then later on once you have the target in the action you can send the set action method accordingly so this is this is the dynamic code that we find all over our frameworks that again simplifies your life reflection this is this is on the hooks that support outlets bindings key value coding again cocoa bindings which have been featured in many of the presentations this is the low-level code that in fact supports those those metaphors so in this case we have selector from string it gets gets the selector and then it asks response to if it does respond to it it performs a selector if no setter method has been found a lower-level hook is provided to in fact lookup the instance variable and set it directly in the object so the simplification for the programmer is you don't have to write setter methods the introspection reflection that's provided with the runtime and Abel's the tools to basically poke at your object and set up the connection set up the binding without your intervention it's nice to have the opportunity to write the setter but that's not required so let's see how are we doing on time here so here are some examples from OpenGL this is on the developer disk here's an example of a more complete set of code where we have a value object that's a cue matrix there that's highlighted and it's surrounded by an outlet right there from interface builder mode alpha again this is on your disk now just because you're writing an objective c++ don't assume that all the classes have to be mixed right here's an example where even though we saw some hybrid code in the previous slide this class is pure C++ there's not any Objective C in it so a very very common thing we see out there is people keeping a lot of their C++ let's say portable and standard and not mixing the models everywhere but in fact just doing it in very special control points right it's very common to want to do a native interface in cocoa and bring over your your C++ engine so to speak and in fact I chats a good example i chat is a wonderful cocoa interface that the chat engine is actually in C++ and they work together via objective C++ so here is just in the example rotating the cube you can see the the one statement there that does the multiplication and then the extra Objective C statements there to do the graphics and update the the scene fairly straightforward stuff now here's an interesting one there was a class in in this example that was largely C++ but there's a couple pure member functions there that we're taking objective-c objects selectors and in fact events so I've highlighted them for you and you could see that these these arguments are passed around as naturally as other arguments would be and so here's the handle event method so you can see that it checks the event type checks if it's a scroll wheel and does the appropriate operations again very very straightforward okay so let's go to the next example which is Core Audio this is a little bit more complicated this this example what's in orange are the C++ objects and what's in blue are the objective-c objects so here we have a few vectors and a map that are C++ vectors and map from the standard C++ library that point off to Objective C objects so there's C++ containers that point off to Objective C objects here's the allocation method so you can see that there's a couple objective-c objects being allocated and initialized with the application delegate and then there are several news which are the vectors being instantiated this is a good time to note that in Objective C all objects are dynamically allocated okay or by reference the only special object is in a string which the compiler knows about so it knows how to create static string constants but all other objective-c objects that you create are dynamically allocated in C++ it's very common to use value objects and we're going to talk a little bit more about that in a bit but here it so happens that the new is being used and their dynamic as well so all the objects on this in this method are dynamic so here's a simple little method that basically sends the push back call we're and gives it the window controller so here's a very simple example the obviously plus plus vector you're pushing an objective-c object into the vector now this method has something tricky about it that we're going to talk about later I don't want to get into it now we'll get back to it here is the destroy method and there's an iterator so just like we have vectors we have C++ iterators being used fairly naturally it's iterating over the vector and it's checking for the controller and when it finds it it erases it from the vector and then it sends the release message okay and that sort of gives you a hint on where the the trickiness is that Objective C at least in the context of of cocoa uses a retain release metaphor currently so because all objects are dynamically allocated cocoa gives you some relief in trying to understand the lifetime by providing a reference counting metaphor that it it in fact implements and encourages you to implement and this is an example so let's get into the gotchas now so we just looked at c++ containers of cocoa objects there i was alluding to the Gotcha which is it's a little bit sticky right now mixing the retain release metaphor with with c++ when the vector classes were developed they obviously didn't know about objective-c right so it's hard to expect them to know how to do this and again I as I said before both runtimes both libraries don't know about each other fortunately c++ provides some interesting hooks to be able to solve this problem I don't know if you're able to read the comments but basically what's happening here is it's considered a no-no in cocoa to transfer ownership across calls okay so what I expected to when I was reading this this method I was expecting to see a release at the end okay but it doesn't do a release it passes passes the object on and for purposes of this talk if you're not familiar with retain release this is probably more than you need to know but here there's a manual release so you saw that and and it's just confusing okay and and I've talked to people who use the language and they want to see this fixed so one of the things that we're going to be doing is adding a smart pointer class to automate the retain release okay so for any of you who've tried to do this and have been confused by it fortunately C++ provides templates which allow you to customize the behavior of the vector so for instance by providing an ID pointer template it allows it allows us to solve this problem and write that code much more naturally and in a much less error-prone fashion so this is a feature that we expect to provide for Tiger if not it's actually fairly easy for you to implement this yourself if you need to do so you know in the short term as I say in the slide boost shared pointer might even work for you off the shelf if it doesn't then you could adapt it to your needs and so on so the next gotcha is exception handling exception handling is supported in both languages in in in C++ it's try catch and objective-c it's a triac catch but one of the confusing things here is if you look at these two calls here one is the sending the process message and the other is timer update you don't know whether these two calls are truly like the first one you don't know if is it C++ pure or is it hybrid right the second one you don't know is this pure objective-c message sent that is the method on the other end is it pure Objective C or not right if you know that then life's a little bit simpler you can just wrap your C++ call with the try catch and wrap your objective-c message then with that try out catch unfortunately in this case they're hybrid which is why I used it as an example so to solve this problem it's it's straightforward maybe initially non obvious but you you basically wrap the first calls in try-catch and that's the C++ catch right there and then you would rap without trial catch okay now fortunately the way exceptions are used in Cocoa and C++ they're really used for exceptional conditions as errors right so you don't find yourself having to write this type of code very often usually in Cocoa in particular you write a you inherit an exception handler from menace application and even if you decide to implement your own exception handling you usually do it a very high level of the application again it's not it's not handled for things like file not found so that what I just talked about was sort of education it wasn't it isn't really a big gotcha it's just something did you have to think about more of a speed bump this is a gotcha this-this-this init method does something very bad basically it has there's a value stack object that I've declared here and as the comment says the destructor for stack object will not be called automatically when that add throw happens okay the reason for this unfortunately is related to the fact that we're trying very hard to be binary compatible so that when we added at try at catch last year we actually use set jump and long jump which is what we have been using in cocoa and such up and long jump do not know about the C++ runtime okay so in fact this is this is a gotcha by design unfortunately so what's the workaround the workaround fortunately is pretty straightforward my clicker OOP okay the workaround is fairly simple all you do is call the quality structure destructor manually not the prettiest thing but you know C++ allows you to do that and it's possible again hopefully you won't be dealing with this this is more a matter of completeness and me being entirely upfront on on some of the little things that that exists again I don't think this really will affect your debt day to day okay errors and warnings this is this is probably the biggest gotcha that I hear complaints about and it's because in C++ value objects are extremely common and if you want to have a value object or an embedded object that's part of an objective-c object you get some really bad errors and warnings that basically tell you crazy things like the V tape the V table has virtual member functions and all kinds of crazy stuff that it just doesn't make sense and it's because again the object runtimes the two object runtimes don't collaborate when dealing with this construct mainly because at the time we couldn't figure out how do we maintain the design point of keeping the runtimes distinct and solve this problem we had incorrectly assumed that it was impossible fortunately we figured out how to do it and we'll be providing it in a future release for now there's there's a workaround right now if you declare them reference and you instantiate them directly it's it won't cause you any grief again because new will do the correct thing and as I said this limitation is is going to be removed and it'll just work as as expected so there are several several people anxiously waiting for that for this one I know and hopefully I'll make their lives easier so I don't know if I went too fast here we're going to fortunately have some some nice about 20 25 minutes left for QA which I really value and given the number of people here I'm sure there's plenty of questions to ask about this but here on are some take-home points I think it's fairly clear that doing this stuff is fairly straightforward the learning curve if you're already a C++ programmer is very low now if you're not a C++ program and you're not an objective-c programmer this isn't the language for you right this is this you know yeah Apple is using this extensively in almost every part of the company whether it be RI apps pro apps i chat safari and many third-party apps i've seen and many that that aren't public they're using this extensively and it it's proven itself so I feel really confident in advising you guys to use this because I think it's going to save you time and I think it's also going to expose people from the C++ language culture to new metaphors new ways of building systems that I think will also benefit you and I think both languages compliment one each each other I am I'm constantly amazed at how they really don't get in each other's way this wasn't clear when we did it many many years ago and in fact you know we were concerned about creating a monster and and I don't think we've done so I think this is a very expressive tool and I think if if C++ would have become the next C which is what I thought 15 years ago when C++ was being developed I was fairly convinced that it was going to be the next iteration of C then this would seem even more natural I think the only reason that it even seems to be a little controversial is because C++ didn't become formally the next C even though it's the de-facto next iteration of C and I find most people doing serious application development are using it in some way so documentation and Matt Formica he's our tools evangelist