Leopard Innovations • 53:50
Learn more about Objective-C 2.0 and how you can take advantage of it in your own applications. You'll receive an introductory, hands-on primer to properties, fast enumeration, and common garbage collection design patterns. Gain a deep appreciation for how Objective-C 2.0 will help you write better, more maintainable, and more concise code. Bring your laptop.
Speakers: Bill Bumgarner, Mike Jurewitz
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript has potential transcription errors. We are working on an improved version.
I'm Bill Bumgarner. I'm the engineering manager of the runtime team or the wizards that make the runtime, so I'm the wizard wrangler. This is Coding Smarter with Objective-C 2.0. I hope you all have your laptops booted in Leopard, full batteries, ready to go. I want to see a show of hands, how many people here have done a bunch of Objective-C programming? Cool. How many of you are pretty new to it? Alright, and while we're raising hands, Michael?
- Yes? Can you lead a wave?
- Wave?
- Wave from over there to over here, come on.
All right. Good. Now that everyone's awake. So here's an overview of the session. We're here to do a little survey of Objective-C 1.0, kind of frame where we're coming from and some history of this platform. And then we're going to look specifically to Objective-C 2.0, dive right into this code. I think the teams pulled together some very cool stuff that's going to make you much more efficient programmers. We want to get your hands in it and give you a feel for it.
So Objective-C. Well, it's a simple set of syntactic extensions to C. And it's truly a set of extensions. It doesn't try to overload or override existing behaviors in C. It is a dynamic object-oriented programming language. And by dynamic, I mean that you have a lot of control of the behavior at runtime and there's a lot of behaviors that happen at runtime as your applications are running, as opposed to all compiled time or all static. It was actually invented about the same time as C++. So this is a very mature language.
There's a small and powerful runtime that's actively available at the core of your applications, its there for you to use to leverage, to build your applications. It uses single inheritance, so there's no multiple inheritance for the implementation side. The inheritance model supports inheritance of class methods too. So you can create a class, have a class method, subclasses, override that class method. However, specifications can be multiply inherited.
And you can extend existing classes with categories. And this is an implementation inherited by subclasses. So you can extend the base class in this object and that implementation will be available everywhere. So when we talk about languages, though, really you never talk about a language without talking about the APIs that are available with the language, the libraries available with it.
And this is very true of Objective-C on the Mac OS X platform. There are other base classes available in Objective-C; they're out there, you can go find them But on the Mac OS X platform everything kind of roots from NSObject as a part of the foundation kit and a lot of what we're going to be talking about today is actually behaviors provided by the foundation, not a part of the language. But the two can't be separated so that's why it's important to talk about them at the same time.
Lots of powerful objects in the foundation kit for you to use so you won't have to reinvent common wheels. Now when we're talking about an object- oriented language, of course, it's all about subclassing, amongst other things. This is a typical class declaration. You see there's instance variables for storage, there's methods, the method with a + in front of it, class method, instance methods, etc.
So we talked about multiple inheritance of specification and in Objective-C that's called a protocol. And you'll see that we've now declared that our Car object which is a subclass of NSObject implements the NSCopying protocol. And the NSCopying protocol is just a way to say my class is going to implement the copyWithZone method. And as well, give the foundation and the other tools and the environment including the compiler the ability to validate that your class implements that contract. Your class is compatible with the copying contract and instances can be copied.
Now categories on the other hand, they're sort of an informal protocol that can fulfill three roles. We can use them as an organizational tool. I think you may have seen in some languages where there's not organizational tools like this. The interface for class can get really huge. Well, we can use a category to break up the interface. For example this is the NSArray's interface. And you'll see here come the formal protocols again.
NSArrays can copy, they can mutably copy, they can be encoded and it's got this base API of count and objectAtInderx which is sort of the fundamental behavior of an NSArray. And then it's got extended behaviors for your convenience. It's got creation behaviors that you can use to create new arrays, things like that. So you can use categories to organize your code.
Or you can use categories to provide extensible implementations. Now this gets back to we can't talk about a language without also talking about the kit of objects. And this is behavior provided by the foundation on NSObjects, KeyValueCoding and KeyValue|Coding extensions on NSArray. But one example of many of these kinds of things and what this is saying is that we've got these additional methods on NSObject provided by some other provider somewhere else, not a part of the language, not a part of NSObject itself, that you can use throughout your code. And you're free to add your own.
As well, we can use categories to define optional or informal behaviors. One of the models you'll see throughout Objective-C, throughout the foundation of the AppKit, Core Data, and the other kits on the operating system, is this notion of delegates or notifications. Well, with a delegate it's kind of like, hey, that object over there, if it implements this method, I'm going to let it know that something's happened, say before and after like in our Car model, maybe the Car will say, oh, I've got a delegate, I'm going to go and call the willOpenDoor and didOpenDoor methods before and after aDoor opens. This is very easy implementation for you to put into your code, very easy behavior to implement. And it can be very powerful. It means that you can avoid subclassing in a lot of cases.
So the other part of object-oriented programming, of course, is, well, you declare a bunch of classes then you've got to talk to them. And that's dispatch, method dispatch, or runtime dispatch of method invocations. In Objective-C, methods break down to basically standard C functions. As a matter of fact, the original Objective-C compiler wasn't a compiler, it was a pre-compiler.
It took the Objective-C syntax and spewed C code that was then compiled by the standard compiler. In particular, when you have a declaration like this, replaceWheel withWheel, the method is called this, replaceWheel:withWheel:. There's no named arguments, there's no keyword arguments, there's no type-based dispatch. Objective-C uses a very simple, and I believe, very elegant model.
And this results in a much simpler runtime. It also means your code's a lot easier to debug because you don't have to sit there and go, okay, I've got a wheel instead of a round wheel, okay, that means that method instead of this one. Very straightforward. So when you call the method using syntax like this, Objective-C uses the arguments interleaved with the method name, again not keywords. This, the compiler will turn this into a call to the objc_msgSend function or one of the other variance, and the objc_msgSend function will look at the target, aCar, for this the C function then implements the selector, replaceWheel:withWheel passing the arguments and when it finds that implementation, it will call it.
Now this is where the dynamic runtime kicks in. Because this is what happens on every method invocation. So if you extend this class later and add a method, somewhere in the class hierarchy, the runtime will figure it out. If you call a method that doesn't exist, you have hooks to catch that and do what you need to do. And in Objective-C 2.0, as we'll see in a little bit, you can actually dynamically add method implementations at runtime. So moving into Objective-C 2.0.
We had some goals with Objective-C 2.0, and they were actually focused on refinement and productivity, not focused on revolutionary changes to your development world to achieve the next generation development environment. In particularly, we looked at the past 20 years of Objective-C and all the patterns that people have used and the patterns that have been standardized upon.
And we wanted to syntactically encapsulate those patterns to give you the ability to write software using less code that would result in code that executes faster and code that was more reliable. And by that I mean code that the compiler has more hints to do the validation to give you warnings and errors that lead you to finding bugs in your code before it's ever executed. We're going to highlight a few of the features in Objective-C 2.0, some changes we've made to categories and protocols, fast enumeration, garbage collection, and properties. And really in a lot of ways this is kind of in the reverse order of the magnitude of change to the language.
So categories and protocols. Well, we've seen categories and protocols are a means of organizing your code, a means of providing informal specification, inheriting specification, providing formal contracts. With categories, a common pattern was that you would declare a set of API for public use throughout your application or somewhere else and then internally to the class you'd have some private stuff that the class used that should never be used outside. And it wasn't really possible for the compiler to validate this fully.
So we changed it so that you can now have a class extension which is just like a regular category declaration but there's no name in the parenthesis and this just says to the compiler, hey, whatever's declared in here, treat it exactly as if it was declared in the main @interface for the class. And thus the compiler will treat the methods and later you'll see the properties in there as if they have to be implemented to that specification. It's not just an informal advisement.
And then with protocols we've added the ability to declare methods as being optional. Formerly protocols,every method declared in the protocol, had to be implemented in your class to achieve conformance. This is no longer the case. So this is a protocol in the AppKit, NSPrintPanelAccessorizing for you know accessorizing your print panels, you can put rings and earrings and beads on it. And you have an optional method here, so to conform to this protocol you implement the first method, you can optionally provide the second method and the compiler can validate everything. Moving on, fast enumeration.
So one of the most common constructs you find in code outside of the setters and getters and accessors which we'll get to in a moment. You've got to enumerate stuff. And the traditional enumeration pattern was using objectEnumerator, then using nextObject and looping through. It was memory intensive because that invocation of objectEnumerator ended up duplicating the collection being enumerated most of the time. It was verbose, I mean there's just a lot of lines of code here, very repetitive and, of course, anything that's repetitive-- error prone. And it was imprecise.
Because you weren't really enumerating the collection that you started with. So there could be mismatch, there could be other bad things happen. So fast enumeration. There's not a lot to say here. It's simply a syntax for an object in a collection that's fast, concise, efficient, and very easy to use.
You can also very easily adopt fast enumeration for your own collection classes. You simply implement this formal protocol NSFastEnumeration which declares a single method countByEnumeratingWithState; objects; count, whose implementation is well documented and will be covered in detail in a session later this week. So with that I would like to bring up Michael Jurewitz to show you some hands-on with fast enumeration.
Thanks, Bill.
My name is Michael Jurewitz. I work in Developer Technical Support, so my main job is actually to interface with developers like yourselves and help to answer your questions on the frameworks. For today we presented some pretty neat but simple projects to take you through Objective-C 2.0. So Bill's intro slides said you want to make sure that you have the IntroObjC2 project downloaded from the website. So we're going to go ahead and go to that right now.
So if you open that up, you'll notice we have three projects here for you. We'll be getting to two of them later. We're going to take a look at the fast enumeration project first. You'll also notice that we have a beginning and an end so at any point during these changes, you lose where I'm at, know that you've got an end point and a reference to go back to. So let's go ahead and open up the beginning project.
So this is a simple application, simple foundation tool that we call PeopleMinder. And what's happening here is we're simply taking a sample plist that's got a bunch of information on people, their first name, their last name, and their favorite color, and we're just going to enumerate through it, first using NSEnumerator and then using fast enumeration. So you'll notice that our person class is actually pretty simple. We just have strings to the first name, the last name, and the color and we've got some pretty basic accessors too.
So let's go back to PeopleMinder.m and take a look at what's actually going on. So the first part here we're just loading up the data, what we're really concerned about is happening in this section right here. We're going to go ahead and make ourselves an array for people to add and we're going to take some snapshots of our start and stop time to figure out how fast we can actually get through this.
So you'll notice we're using the usual NSEnumerator construct using the objectEnumerator and a simple while loop for getting our next object. So obviously changing the syntax isn't that difficult so Xcodes actually built in some code refractoring just for us. So we're going to go ahead and do a Convert to Objective-C 2.0. Make this big, we'll make sure we have the Modernized Loops checked, and let's go ahead and take a preview and see what Xcode finds for us.
So you'll notice that it found two changes that it would like to make. And it gives us a nice div of this too. Scroll down here. So you'll see that Xcode wants to get rid of the objectEnumerator line and change our while to for in, it really is that simple. So let's go ahead and have that done.
So I'll make that change, and you'll notice we've gone ahead and made the change here. So now, let me go ahead and quickly actually undo that just to show you what the timing looks like using NSEnumerators. Let's go ahead and do a quick build and go and we're going to go ahead and take a look at our console.
- You've got to resize the window. Oh, sorry.
- Just resize it. There we are. There you go.
- Thank you. So you'll see we took that long to get to the loop. All right, great. So now let's go back here, we'll go ahead and do the, why don't you do this by hand this time-
- this is a really easy change. Take out this line, make this for( theObject in dateArray, save that off, let's go ahead, let's build and go again.
You'll notice we have dramatically increased the speed with which we've actually gone through this loop. Fast enumeration really is fast and the real win is avoiding the copying. It's a really easy construct to use, it's as simple as that, it's going to make your code a lot faster too. Bill back to you.
- Thanks, Michael. Sure. So moving on, that was fast enumeration. It's fast and it enumerates.
- You need to go back to the demo machine.
- Oh, I'm sorry, can we go back to the demo machine for just a moment please?
- Sure.
- Thank you.
- Yeah, no problem here. Go ahead and I'll build and go here again for you and try to get that text bigger. Let's see here.
- Command T.
- Command T.
- (inaudible)
- Yeah, he's going to make the font bigger.
- Yeah, right.
- Or maybe not. Shift command +?
- That's okay.
- So the results, zero point zero, zero, zero, zero seven three. There we go.
- So in this case, you see this is actually fast enumeration that took this time. But in this case we actually were roughly thirty times faster than regular enumeration. All right.
Back to you, Bill.
Thank you. So a lot of that speed was actually the copy. So not only was there a speed boost there, there was also a memory footprint reduction. And you could especially see that if you were, say, enumerating a set of fairly complex stuff or enumerating a collection that has a very inefficient copy or enumerating an infinite collection making a copy of it would be bad. So garbage collection.
In Objective-C 1.0, memory management which has been the paradigm from memory management in Objective-C since 1993, was reference counting. Retain, release, autorelease, and the "pool". These were methods on NSObject. They were not language behaviors, they were runtime behaviors, they were object behaviors. And they were supported by the NSAutoreleasePool class.
It's actually a pretty simple concept, its just reference counting. With this added little thing that you can delay the decrement of a reference count, until sometime later via the autorelease pool. Throw it in the pool, if something else pulls it out, hey, it will stay alive, if not it goes away.
Except for autorelease pools were per-thread which as we've moved into this modern day and age of pretty much every Macintosh shipping with multiple cores, threading becomes a big issue. And that perthread notion of autorelease pools, yeah, ouch. And garbage collection, what we've done in Objective-C 2.0, is add a collector that's conservative, so it never moves stuff around on you. Your pointers will always stay the same. It's generational which is an optimization from the eighties, I believe, yes, the eighties.
That basically means that it scans a limited sumset of the objects based on how long the objects have been in existence because in general short-lived objects are common, longer- lived objects tend to stay around for quite a long time. And it's concurrent, which means it's running on its own thread, it doesn't block the other threads, and it's a very efficient collector.
It's also an opt-in contract which means you can choose whether or not you want to use GC. It's all or none, though, meaning that if you choose to use GC in an application, everything in that app needs to be GC or not. However, you can write dual mode code which means you can continue to write the retain/release/autorelease code and still use it under GC. This is exactly what we've done for all of Mac OS X, so you don't have separate copies of the framework binaries for GC versus non-GC.
Not necessarily the path we're recommending. Veing able to choose GC only is very liberating, and one of the added benefits of garbage collection is that it simplifies threaded programming The reason why is because now object life span is below the behavior of the objects themselves. Object life span is largely governed by assignments.
Assignments are atomic so when you say my stack ivar equals some random object, that's a reference, it's going to keep that object alive. You don't have the race conditions associated with multiple method calls to try to put things in a pool, pull them out, retain, release, all that. Or the exception handling that's required therein.
So when you're writing the dual mode code what basically happens is the retain, release, and auto release are ignored. Literally they are rewritten to become effectively no ops. The one exceptions is the -drain method on NSAutoreleasePool. It's kind of a hint to the collector. It says, hey, you're back in the non-GC world, developer thought right about now is a good time to get rid of a bunch of temporary objects so the collector uses that as a hint.
There's also what are called zeroing weak references, which means that you can have a strong reference to an object, which is ownership, it will keep that object alive, the collector won't collect as long as the strong reference exists, or he can say, this reference is weak but unlike in the non-GC case where when you release an object for the last time, all references to it become dangling pointers, the collector will actually sweep back through and zero out any of the weak references to that object.
( Applause )
Yeah. It's really cool. In particular, it means that you don't have to worry about removing observers from NotificationCenters on object death.
( Applause )
There are new collection classes provided in the foundation that have been optimized for garbage collection, so mapping and hashing collections. They work in non-GC as well, and like I said, no need to unregister these observers, I can't tell you the number of times I've seen things crash in NSNotificationCenter post notification. It goes away now.
You do have to optimize for a little bit different model of memory management. Whereas before you had the retain, release, auto release and you had really tight control over object life spans and memory bloat could often be fixed by adding an autorelease pool in a loop somewhere. With the collector it's a little different.
You're caches will behave a little bit differently. You can take advantage of those weak references to make caches self cleaning. You can, you want to sometimes avoid lots and lots of allocations of small objects reuse in some cases, things like that. But it's all just an optimization exercise.
One of the nice things about GC is that when you're working with GC and debugging GC you don't generally have dangling pointers to dead objects so when you're inspecting things in GDB you're actually talking to a viable object and generally trying to figure out why the heck it's sticking around when it shouldn't be anymore.
And there will be lots and lots of information on the collector. It's an amazing piece of technology and I encourage you to go to the session later on in the week. So with that, Michael, would you like to show some hands-on with GC?
All right. Thanks, Bill.
Okay, we'll be turning to the Garbage Collection project now. So go ahead and go to our Beginning project and open that up. You'll notice that this has changed a little bit, we've actually got a full-fledged GUI application we're using at this point. So I'm just going to build and run.
And you can take a look at it. So a really simple user interface for application, we've got a list of people, we've got their colors, we can go ahead and go through these. We can use the Inspector here to change the color that we have listed. Really simple application.
So now let's go ahead and let's introduce garbage collection this application. So what do you have to do in your project to do that? Well, you simply need to go to Edit Project Settings under the Project menu, go to the Build tab, and then we want to make sure that we click on Enable Objective-C Garbage Collection. And there you go. When you compile the collector at runtime is going to be working for you.
Now as Bill mentioned, retain, release, and autorelease all are basically no ops under garbage collection. So you'll see at several points here we've got releases, we're doing memory management, we can actually go ahead and still build and run this application and the application is still going to behave just as it did before.
So again, just like you saw. But one of the great wins about garbage collection is the amount of code that you can actually tear out and that you don't need anymore since you're not dealing with reference counting. So let's go ahead and we'll start in the AppController here and take a look at what we can tear out.
So you'll notice first of all, we make a newPeople array for iterating through our collection and storing people into it. And then this, we're actually allocating newPeople objects and adding them to that array. So one of the first things that we have to make sure we do since we alloc and inited this person is to release it after we add it to the collection. It's a pretty common error to add something to collection and forget to release it. I know that I've done that before, I'm sure all of you have too. But that line can simply go away. So go ahead and take that out.
And now also with our newPeople array after we go ahead and set that as the array of people we want to use we had to remember, oh, that's right, we're retaining that as the app controller so we need to release it since we alloc and inited it above here. We can go ahead and take out this line as well.
Pretty nice. Now with the accessors here, I'm sure all of you have been making sure to use the retain autorelease pattern in your code, of course, so one of the nice things about garbage collection is that since we can guarantee atomicity we don't need to jump through these hoops with our accessors. We can go ahead and just return the instance very well directly. So we can get rid of this and just return people instead.
Similarly we'll go ahead and tear out this code here where we were releasing our old person, assigning a new value and we'll go ahead and have this be people is equal to a copy of the newPeople. In this case we're preserving our copy just to maintain object encapsulation.
So we can go ahead and save that. Now you notice we've actually made a small error in this project. I've been coding in with garbage collection now for months and months, you'll notice we don't have a dealloc method on our app controller. Purely a mistake but again it's one of those things that once you adopt garbage collection you don't need to have in your code. We'll see that in the person class. Let's go ahead and go there.
You'll see pretty simple what's going on here as far as dealing with a few string instance variables and in this case we're actually using an NS Color for our favorite color this time. So as we saw before in the app controller, let's go ahead and take a lot of this stuff and we can go ahead and just tear it out, and replace it with some much more readable code. So we're going to go ahead and replace our firstName accessors with much more simple variant. Same thing with our lastName accessors, again deleting the extra code here. And for our favoriteColor we can do the exact same thing.
Our instance variable here is named color of course so that's why we've got the name difference. Now it's interesting to note that NS Color is immutable so that's actually why we're not making a copy of it, we can just go ahead and do direct assignment. But again down here in dealloc we're doing all this memory management where when we no longer need the objects we're having to clean up everything that we've had a reference to. Well, under garbage collection we can just get rid of that.
So now you notice we've got a much, much more simple person file to deal with. So let's go ahead and save this. We'll build and run. You'll see the application works just as before, no problems, no memory management that we have to do manually. Bill, back to you.
Thanks, Michael.
( Applause )
Moving on. Properties. Properties were kind of quite a bit of discussion. We changed properties on you since last WWDC by the way. Sorry about the change but I think you'll like it better. So in Objective-C, as you saw with the code Michael was working with there, setters, getters everywhere.
I mean in Objective-C you have this standard pattern of setters and getters that are used to set the state and get the state from objects. And when you see it throughout model ops, you see it throughout the view classes for setting up different parameters for visualization. Core Animation is pretty much all setters and getters for setting up the animation parameters. Very common pattern.
And again you'll also see it that a lot of times you'll have a read-only parameter. See publicly it can only be read like the speed method here but privately there's probably a setSpeed method declared in one of those categories again there. Informal category that the compiler can't validate so we'll fix that too.
So when you start getting into more complex classes, this is not intended to be readable, this is just the reality of the situation you end up with large class files with lots of stuff in them that's kind of noisy. And this is a common pattern. Pretty much every class has some amount of these.
So really properties are all about encapsulating this stuff, properties are about simplifying it. We look at the setters and getters, they're everywhere, they are a foundation of the classes you design. They do already work, you know they're used everywhere for data storage, configuration, data flow, and by data flow I mean not just setting and getting stuff but you see key value coding and key value observing and Cocoa bindings, all are built on top of the setter getter foundation.
Why are we fixing them? Well, they're also one of the largest points of frustration, largest points of failure, because they're so tedious, they're very repetitive. Change one and you've got to change the method in a couple places. You know, you change the contract for storage, you've got to change it in several places, it's just, it's easy to get wrong.
And concurrency, threading, greatly complicates the implementation. Earlier when Michael showed the code that was doing a retain autorelease to return an object, some are under the mistaken impression that that is thread safety, that's not. That's an object level behavior. If to make that setter get her pair thread safe, you need to make it really ugly, you need to add locking, you need to add exception handling, you need to have an external mechanism for initializing the locks. It can get very complex very quickly, all adding to the error-prone nature of it.
So if we had these setters getters everywhere, and we want to fix them, how about this instead? We've added in properties, this @property notation, so this and this are equivalent. But this is a lot more, I would say, evocative, precise in terms of the contract, It's the syntax that declares not only the presence of this data accessor's pair, but the semantics of how the data is managed.
And you can see we also converted the private interface to no longer be a named category but actually one of the extensions to the class so the compiler can validate it properly. And you actually see here we're also adding readwrite to the property so we're actually extending the behavior of the property internally, we'll get to that in a second. So what this means is that when you have a more complex class like this we can reduce this code down to this, which is great for Keynote because now you can read it.
So properties, they are a simple syntax that has very powerful consequences. They are highly configurable, they just work out of the box but you have a lot of control over the configuration of them. They can be automatic for convenience, which we'll show in a second, however, you can manually implement for very tight control in the cases where you need it. Or they can be fully dynamic.
And by using properties you are automatically achieving compliance with what is necessary to fit into that Cocoa foundation. To provide a foundation on top of which very rich Cocoa application can be naturally developed. In particular they are compliant with key value coding. Compliant with key value observation. Core Data uses properties extensively in Core Data 2.0 with the NSManagedObject. Bindings which is implemented on top of KVC and KVO uses properties.
When we look at the properties implementation or the properties feature itself, there's two pieces, there's the interface, the declaration piece, and there's the implementation piece. So looking at the interface of the declaration first, the syntax is simply @property, some list of attributes, a type, and the variable name.
The attributes control the storage semantics, in particular, the attribute readonly/readwrite. You can use one or the other, controls whether or not that property will be readonly or readwrite. Whether or not the property implies, in this case, that both speed and setSpeed, the methods, will be available or if just speed will be available. Readwrite is one of the few attributes that you can override in subclasses or in class extensions. So you can publicly declare that a property is readonly and then without writing any code you can redeclare it internally to your class to be readwrite.
Assign, retain, copy, these are the memory management behavior controls so in this case, we have a passenger's property. It's of type NSArray by providing the copy attribute. That will be copied when it is assigned. Now the difference between assigned and retained is a GC versus non-GC issue. Retain is a strong reference in non-GC, it's a retained reference, and a strong reference in GC.
Assign is a weak reference in non- GC, meaning it's just an assignment. But a strong reference in GC and that kind of gets back to the delegate pattern in the AppKit, and I'm not going to go into detail on it, this is something you can find out a lot more information in the Advanced Objective-C session.
So there is a non-atomic attribute and what that means is that when we automatically create implementations of the methods for you, which you'll see in a second, the implementation will not try to be safe under concurrent environments. Now safe under concurrency means not necessarily thread safety, it means that when you call it from a multithreaded environment, being atomic means that you'll always get a value back that at least doesn't cause your program to crash, you're not going to get like a released object or something like that. You can't do thread safety at this level of an API. You just can't. So this is about consistency of values returned. Nonatomic is much more efficient under non-GC.
Under GC, everything's atomic for free except for structures. This gets back to GC adds a lot of efficiency to your applications. So moving on, setter equals, getter equals, sometimes if you have a property name, say, goingFast, you don't want to have a couple methods named goingFast and setgoingFast.
You may want the query method, the getter, to be isGoingFast, and setter equals and getter equals allows you to supply the method name or the selector that should be generated or used by the property subsystem. So as you can see, that gives you a lot of control over how property is declared. Now on the implementation side, there's a new keyword @synthesize. Say @synthesize property name in your @implementation block. And it will synthesize the methods necessary to fulfill the contract that was declared with the @property declaration.
So if you just say @synthesize goingFast, and it was a straight property declaration, you're going to get goingFast and setgoingFast as two method implementations that will work just like they were declared and implemented by you. You can also, say, that the method implementation should be synthesized to use an instance variable other than the same name as the property.
Because of the way the 32-bit runtime works, or what we call the, I guess, antique Objective-C runtime works, you have to actually declare an instance variable as storage for a property when using @synthesize. In the 64-bit runtime or the new runtime, the 2.0 runtime, that's not the case and again you can see more details on that in the Advanced Objective-C session.
So anyway getting back to this, you can synthesize a property that uses a preexisting instance variable of a different name or you can say @dynamic property, and what that tells the compiler is to not complain when it doesn't find the implementations of the methods that the property declaration implies.
Because you're going to take care of bringing those into being later. Maybe you'll dynamically provide them at runtime, maybe you'll load a bundle that has them, I don't know. And then finally you can just implement the methods yourself. You can implement set speed and, you know, speed directly. We've also added one additional bit of syntax for making property use much more convenient.
So say we had @property speed. You could call aCar speed using the normal Objective-C method syntax and it would work just fine. However, we have overloaded for the first time one piece of C syntax, the . to allow property access and actually any method that fits the pattern. So that you can say aCar.speed. And that is effectively equivalent to calling the speed method. There's some little more subtlety there, again, the Advanced session covers that. Or you can use this for assignment, aCar.speed = 87 which is more or less equivalent to aCar setSpeed:87.
So, Michael, hands-on properties. This is one of the more exciting additions simply because of the reduction in code.
That's right, you stole my line. But, yes, no, properties are, properties really are excellent for how they're going to change your development. So let's go ahead and dive right in and have a look at that. So open up our properties project, go to our Beginning project, and take a peek.
This is, actually, the exact same application we had at the end of the garbage collection section. We just copied it pretty much right over. If you go ahead and look we've made a few little changes to how we've actually done a few things just for readability's sake. One of the nice things about garbage collection is that in something like your init method you could just say NSArray array, keeps you from having to say alloc init, a little more readable.
I like that style a lot better. We're here to focus on properties right now. So let's take a look at AppController.h. And notice we've got our people that we've been holding on to, as well as a couple accessors for dealing with them. So let's go ahead, and first things first, get rid of those.
And let's add a property declaration for the people array, so we'll say @ property, we want this to be readwrite. You want to make sure that we're making a copy of it when we go ahead and get a new value. It's an NSArray, and we're calling it "people;. Simple as that.
So now let's go to the App Controller, let's go down to our accessors here, and you can see we're just doing some pretty vanilla stuff here, we're returning a value, we're making a copy when we get it, so properties are going to go ahead and provide this for us. So let's go ahead and get rid of this repetitive code and take it out of our implementation. And we need to make sure that we actually synthesize values for these properties so we're going to say, @synthesize people.
That's all we had to do to tell the compiler that we want it to generate these methods for us, have the runtime generate these methods for us at runtime. So now some nice little syntactic changes we can make here. Down here we say self setPeople:newPeople. As Bill discussed that's still going to work for us, we can leave that code exactly as it is, but one of the nice changes you can make is we can take this line and we can make this self.people = newPeople;.
Now one of the important things to note about using this syntax, it's tempting to think of this as direct ivar access, this is actually just a synonym for going through a message sent, this is actually a message that's being sent at this point. So we're actually going through the setPeople method in order to use this. But again one of the nice changes we can make as a result of properties.
The real win though happens, and I think you'll see this more often than not, in your model classes. So in this case, again, we had our person, we had a first name, a last name, and a color. Remember this is a pretty basic class as far as things go, so really not that many accessors or instance variables happening here.
So let's go ahead and rip out these accessors because we're not going to need to write these manually anymore. So we're going to say @property readwrite, copy) for NSStrings. We want to make sure we're making a copy of the string that gets passed in. In this case, we want firstName and we want lastName.
Now we also want to make sure we have a property for our color so we're going to make sure that this is readwrite, we're going to make this assign, remember again, we're operating under garbage collection as is NSColor. In this case remember our API will refer to this as favoriteColor so that's what we're going to put here. So we'll go ahead and save that and go to our Person.m file.
Now first things first, let's go ahead and synthesize these things right up front. So we'll synthesize, in this case we add firstName and lastName and we also had our favoriteColor but remember our instance variable is named color. So we want to make sure we tell the compiler about that.
So now down below we've got all these accessors that again are just doing the basic set of tasks for us. So we can take all this code and just rip that right out. You see we've already dramatically simplified your implementation, made things a lot easier to follow, and again, here we can actually make some changes if you wanted to, to the init method where you could actually say self dot, self.firstName = first, we don't need to do the copy here because we know that the property is doing a copy for us. And the same thing with lastName. Now here we can do the same thing for color and we have to make sure we remember that we called it favoriteColor.
So let's go ahead and build and run, and you'll see the application still works exactly as it did before. In this case we've been able to tear out a ton of code. But let's think about this. This isn't necessarily realistic, there's great reasons to have accessors a lot of the time. You actually need to do some complicated things and your accessors set off other behavior, etc.
So let's go ahead and let's put one of these accessors back in, so let's go ahead and let's do the setFavoriteColor. So we'll say setFavoriteColor, and we're going to be taking in an NSColor, and just as before we're going to do a simple check to make sure that we've actually got a new value, at least as far as pointers are concerned.
And here we're just going to say color = newColor, and then we'll just do a simple NSLog to see that we've actually got a new value. So we'll say the color has changed, and we'll make sure that we log out the newColor. All right, so let's save that, we'll build and go, if I bring up the console here I promise I won't make you try to read it. You can just see that there's text.
And we see if we go ahead and try to change this value that we're going through our setter automatically. Meanwhile, we've dramatically reduced the amount of code we had to write, made our project much easier to navigate in the future, and more maintainable. All right, Bill, back to you.
Could you leave the code up for just a second, please? Heather, go back to the code, please? Thank you. So not only do you see a reduction of code, there's one other thing I want to point here,.
We reduced repetition, we reduced fragility. If you noticed before when there was the setter of the firstName and the lastName directly to the instance variable, in the init method on here, it was calling firstName copy there, as well as then having to reimplement it in the accessor method.
But now with this code we've actually reduced one of the calls to copy so if you wanted to change the semantics of how the firstName and lastName are managed so that they're no longer copy, there's exactly one place to change it now and no places to forget to change it.
And that's about reliability. Another aspect of this is that we're never boxing stuff. With key value coding, key value coding is intended to be a bit of a higher level thing. And it does boxing which means that when you're passing numbers through it, it's sticking them in NSValues.
And that's very appropriate and the correct behavior for that level. When you called key value coding directly, which was a convenient thing to do when looping through, say, a set of keys or something like that, setting up values, and you said, you know, take value for key you commonly had to put an integer or Boolean into an NSNumber. With properties you don't have to do that. It can handle the scalar C types and structures just fine.
That has two advantages beyond the efficiency, well, one advantage beyond the efficiency. It means the compiler has the type information such that if you do a type mismatch or you do a type quer-- or conversion that's lossy, the compiler can tell you about it. And that gets back to doing more reliable code. So not only have we seen a vast reduction in code, the compiler can now tell you a lot more about the quality of the code and help you to achieve bullet-proof, bug-free programs. That's pretty cool. Slides, please.
So later on today there is an Objective-C 2.0 Lab and then we're going to redo another Lab tomorrow. So we'll be there, please come by, say hello. There's two more sessions focused on Objective-C. There's an Advanced Objective-C 2.0 session which is going to cover an area which I only glossed over, which is the 64-bit runtime or the new runtime and some of the features that are available there where we can add these features without worrying about binary compatibility. It's pretty amazing.
It's definitely a look to the future of Objective-C. And the Garbage Collection session at the end of the week which will go into the garbage collector in depth, talk about debugging, analysis, and fine grain control, as well as having to look at some of the architectures you can use specific to the collector.