Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2006-320
$eventId
ID of event: wwdc2006
$eventContentId
ID of session without event part: 320
$eventShortId
Shortened ID of event: wwdc06
$year
Year of session: 2006
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC06 • Session 320

Refactoring in Xcode

Development Tools • 54:29

Xcode in Leopard introduces refactoring for Objective-C, a powerful way to help you improve the readability and maintainability of your codebase through automated transformations. Learn about the variety of refactoring operations that Xcode supports, and gain an understanding of how to use Xcode's new capabilities.

Speakers: Robert Bowdidge, Andrew Pontious

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

My name is Robert Bowdidge. I'm a member of the developer tools team at Apple. And I'd like to tell you about a feature that the team I work on has been involved with, refactoring. To give you an idea about what we're going to be going through today, we're first going to start off explaining what refactoring is and why you should be really excited that you now have access to it in Xcode. Second, we're going to give you some demonstrations of what refactoring in Xcode looks like, so you can see how it works.

And finally, we're going to give you some details about how you can actually refactor your own code and some of the things that you should be aware of as you're doing so. Now, before I start, how many of you actually know what refactoring is? How many of you have used refactoring for other languages? How many of you have used refactoring for C-like languages? Better than I thought.

Okay, what is refactoring? For those of you who haven't seen it, refactoring is a way to make global, structured, controlled changes to your source code. By global, that means that we actually have ways that can go through your entire program and change all of your source code in some particular way. By structural, we mean that the changes involve either your design or your structure of your program, what the names of things are, what abstractions exist, and so on.

And they're controlled because a refactoring tool gives you ways of changing the code in ways that maintain properties that you really want, such as whether the code compiles the same before as after, as well as whether it runs the same before as after. And refactoring is really cool because it gives you a way to make your code more maintainable, and it gives you a way to make it easier to enhance your programs to add new features.

Now, to give you some examples of that, here's the most simple example that most of you probably have seen before: the idea of renaming a declaration. So, let's imagine that you work for a company that does car rentals. And one day your boss comes in and says, "Hey, I've got a great idea. Let's rent trucks." Now the problem is that you already have this class called CarRental that represents the rental record.

And the problem is that for anyone else on the team who sees this, they may think that this means only cars. But when you go to trucks, when you're renting cars and trucks, does this class only apply to cars, or does it apply to all the vehicles that you're renting? And so what you'd like to be able to do is change that name and replace it with a new name that's more meaningful. And you'd like to make sure that by doing so, you're not breaking your code.

A slightly more complex example is the idea of partitioning a class, breaking it up into multiple classes. So for example, with this rental car example, you could imagine the idea that you have a car class representing the cars that you're renting. And there may be parts of that that are generic, that involve things like what the mileage is and when the next repair is. And there may be parts of the class that are specific to cars, such as how many seats it has, and whether it's a convertible or not.

And so, if you were actually going to add an enhancement to be able to add the idea of renting trucks, this model's not going to work, because you already have the car stuff embedded in there. And what you'd instead like is something like this, where you break your car class into two parts, one being the vehicle generic behavior and one being the car-specific behavior.

You make the vehicle class a superclass, and then you subclass the vehicle class to make your truck behavior, and then you add your truck behavior in there. And this is how refactoring gives you a way to make your code more maintainable, by making it easy for you to do this.

Now people do this. How many of you actually have refactored by hand, not with a tool? It's like everyone. I should just skip the intro, but I'll go through it anyway, just for practice. And the reason why refactoring is hard is because it's such a pain to do. People will do this.

You can look at Fowler's book, and he has all the examples about how to actually do these kinds of changes by hand. To be able to refactor by hand, you have to make sure the code compiles, you have to make sure it behaves the same, and that means you actually have to run your unit tests, assuming you have those.

Then what you have to do is, once you've figured out how your code runs, now you want to start making the change. And one of the things you need to make sure is that you understand what the constraints are on the transformation. What are the preconditions that need to hold, and what are the things that could keep you from doing the transformation? For example, if you're renaming a declaration, are there already declarations with that name somewhere else in the program? And you want to be able to check that, so you have to go searching through your code looking for that.

Then what you need to do, of course, is you need to go to every single place that could possibly change in the source code. And you need to look at every single spot, and you need to decide what change needs to go there. And you need to decide exactly whether that change deserves to be there. You need to make the change correct. And that's really detailed, and that's really nasty. And in fact, I had a friend in graduate school who tried something like this with the Sun OS kernel. He was adding some networking code.

And he had to go through every line of the networking code, and he had to figure out what was going on with a particular buffer to figure out how the behavior was going to change. And he spent three days going through all that source code. Because he couldn't stop, because he'd forget what he'd been doing before, and he didn't want to lose his place. And that's why we need refactoring. And finally, once you've done your change, you actually need to compile it and test it and make sure it actually behaves the same as you expected, just to make sure you haven't broken things.

Why should you have to do this? Although we're programmers, we can't read source code that well. We can't look at 100,000 lines of source code and remember all the details. We can't make changes across a million lines of source code easily. But we've got tools that can. We've got things like compilers. We've got things like the IDE. Xcode does code completion, and it can read my source code.

So why can't it do the work for me? And in fact, I, as the programmer, know what my design should be. I should be able to say, I want my program to look like this. And the tools should be able to do all the nasty, dirty little details of actually making those changes.

And as we've all seen, as we've seen in this room, there are these kind of tools for other languages, but in general these tools have not been available for C-like languages, or at least not broadly. And so for me as an Objective-C programmer, and a C programmer, I don't have access to these tools, and many of you don't either.

So what we're here to tell you about today is that Xcode now has refactoring support. And better than that, we don't just have support for changing source code, but we also understand the Apple environment. And so that means that not only are we going to modify your Objective-C and C sources, but we're also going to change the non-source-style files that exist in your programs. For example, if you have an application with a user interface, you have nib files.

And nib files have mention of interface variable names, it has mention of classes. And so if you were to rename a class, you need to make sure you rename the stuff in the nib file as well. And we also have these things like key-value coding, where the name of a method is related to the instance variable that it manipulates. And so if we change the instance variable, we need to change the method. And finally, we've added support for being able to do some transformations to move things into Objective-C 2.0, so that you can actually start moving over to the new language.

Now, the operations that you can do on refactoring, as most of you know, are called transformations. We call them transformations because they're mathematical. They're converting the program from one form to another, while maintaining all those properties of keeping the code running the same and compiling the same. The transformations that we're supporting in this first version that's coming out with the Leopard Preview are the following. First of all, we have rename, so that you can rename declarations. Second, we have the idea of manipulating class hierarchies, so that you can create an abstract superclass and start moving instance variables and methods up into that.

Third, we have the idea of encapsulating a variable. Right now, it's going to be fields of structures or instance variables of classes. And finally, we have the transformations for manipulating Objective-C 2.0 programs, specifically modernizing iterations over NSArrays to be using the new loop format, as well as converting instance variables into properties.

Now, to go into detail about these transformations, I'd like to bring Andrew Pontious up on stage to demonstrate some of the things that we're going to be doing. Robert Bowdidge, Andrew Pontious Now, the operations that you can do on refactoring, are called transformations. The first thing that you can do is to create a variable. The first thing that you can do is to create a variable. The second thing that you can do is to create to demonstrate these.

Thank you, Robert. So as Robert said, there are four sets of transformations in Xcode. I'm going to start with rename. Now, in order to have a project to use to show you these transformations, we're going to bring together an application which stems from the following problem. Now, how many of you have had any trouble this week finding the right room for a session that you wanted to go to? Okay, some of you.

Or maybe you know where the room is, but you need to know how long it's going to take for you to get from where you are to that room, so you don't miss any of the wonderful content we have for you this week. So, the problem is getting to your conference room.

Now, there are plenty of resources out on the web to help you get from, say, your hotel room to the conference center, but once you get inside the building, you're on your own. So, the solution that you and your buddy came up with was a new application called Get a Room.

What? OK, so the Getter Room application consists of a variety of classes. The first class represents the entire structure that you're navigating through, so that is the conference center class. And then, because conference centers consist of a number of floors, there's also the floor class. And then because floors consist of a number of rooms, there's also the room class. And then, to round out the list of classes, they're the classes that have to do with getting from one floor to another, and these are the escalator floor, excuse me, the escalator class, the elevator class, and the stair class.

Okay, so in order to transform any of these classes, or anything in your project, you need to follow these three steps. First, you need to select the text that represents what you wish to transform. Second, you need to preview the transformation using the refactoring window in Xcode. And third, you need to apply the changes to your project. So we're going to go ahead and show you that. So if we could switch to the demo machine.

Okay, so now, when your buddy saw the Get a Room application that you had written, your buddy noticed that all the logic for getting from one room to another was not just for conference centers. It was also very general purpose for any other structure that you were in, for example, a museum or a skyscraper, things like that. So when you finally marketed this application, you made it a very general purpose application, and you got more sales because of it.

But you know that down in the source code, there's still a class called Conference Center. Now, this doesn't disrupt your code, the first version of it. Everything still compiles and runs just fine. But when someone else comes to the code later on and wants to make new changes, wants to add new features, they might be a little confused at the fact that the name doesn't match its functionality. So we're going to go ahead and rename that class now. OK, so we're going to follow those three steps, the first step being select the text.

In our case, we're selecting the class name Conference Center. The second step was to bring up the refactoring window. There are actually four ways to bring up the refactoring window. The first way is through the Edit menu. There's a Refactor menu item there. The second way is to use the keystroke shortcut, Command-Shift-J. That's probably the fastest way. The third way involves clicking on the refactoring icon in your icon toolbar.

Now, by default, there's actually no refactoring icon there, so in order to add it, you need to customize your toolbar. and add up that icon to your toolbar, then you're all set. Now, the fourth way is just to bring up the contextual menu of your selection. And again, there's a refactor menu item there. So OK, we're going to pick away, and we're going to bring up the refactor window.

Okay, now as you can see, the UI there combines to form a sort of sentence that describes what you're going to do. In our case, we are renaming Conference Center to a new name. So this new name, we need to type it in, and in our case we're going to use "building" because that's a more general purpose name for the class.

Now, as you can see, there's a few other pieces of UI here that we'll get to in a little bit. There's the Related KVC Members checkbox. There's also a Snapshot checkbox. Now, we'll get to the Snapshot checkbox at the very end. So right now, we can click the Preview button.

Okay, now as you saw, there was a little bit of activity going on there in the refactoring window. That activity shows you that we are in the middle of setting up, putting together that preview for you. So now the result here is there are several classes which have checkboxes next to their name, and a tally of the number of changes that are being made to those files on the right-hand side. So that's a good overview of what we've done, but if you want to see more details, you need to select one of those files.

So as you can see, selecting the file brings up a view that shows you what the changes are that we're going to make. In this case, we're changing the class declaration name from Conference Center to Building. If we go to the .m file, You can see we're also changing the class definition name. And if you go to the third file, you can see that we're also changing a usage of the Conference Center class name to the new name. So that all looks good. Let's apply those changes.

Now, if we go back to the project window here, you'll see that the number of files that show as being edited are the same files that we showed as having changes in the refactoring window. What we've done is we've applied these changes by editing those files for you. Okay, so that was our first transformation. If we could go back to the slide machine.

Okay, so now that we've gone through our very first transformation in Xcode, I'd like to go through the features of the refactoring window in a little more detail. So the refactoring window consists of three areas: the setup area, the results area, and the comparison area at the bottom. The setup area comes up first, and again, as I said, the UI there shows you a bit of a sentence to describe what's going on. In our case, rename Conference Center to the new name.

Now, while rename is the default transformation that comes up in this area, you can also bring up all the other transformations that are possible. Now, this pop-up button menu shows you all of the transformations, but only the transformations that are valid for your current selection will actually be active and be something that you can select. So now, if we hit the preview button, as I said before, now the results area comes up. Now, the files in the results area have different state depending on what's going on with them.

If they are inactive, that means that we haven't quite gotten to them yet in the preview. If they're active but have a little progress indicator next to them, that means that we are in the process of analyzing that file right then. And if there are checkboxes next to them, that means we are done analyzing them, and we know what kind of changes we want to make to them.

So again, if you select one of these files, then the final area comes up, which is the comparison area. Now, you might notice that this comparison area looks a lot like another application that most of you have probably used, which is file merge, right? So there are, of course, similarities and differences between file merge and this area.

One of the differences is that this area is coordinated with the file row in the results area. So you see that there's a checkbox there in the file. That means that all of the changes that by default will be made for this transformation are going to be made. And you can see this as well because the arrows all point from... Left to right.

Now, if you say, OK, for this particular file, I've decided that I don't want to make any of those changes, you can uncheck that checkbox. And now, all of the arrows have switched for pointing from left to right to right to left. And this also shows one of the other differences between this area and File Merge, which is, in File Merge, when you want to see the results of the choices you've made for a particular merge, you have to look at the bottom pane, the third pane of a File Merge window. But in our case, we only have the two panes. So we show the results of the choices that you made only in the right pane.

So if you say, OK, I don't want to make any changes, now all of those changes disappear from the right-hand side. But if you come back and you say, OK, well, maybe I want to make that first change, what you do is you select that row and you flip the arrow using the arrow keys, the same way you would do in File Merge.

And now that one change is made. Now again, we have synchronized with that checkbox. So the checkbox now, instead of showing unchecked, now shows mixed. Some changes have been made. Some changes haven't. So if you go ahead and say, OK, now I'm going to make that second change as well, now all the arrows point to the right, and the checkbox is fully checked.

OK, so we've had a bit of a look at the refactor window. There is, in fact, still more to look at in the rename demo. So if we could go back to that demo. Okay, so for our second shot here at Rename, we're going to go to the Room class.

We bring up room.h. And one of the instance variables here is called picture. Now, if we look at a sample picture in this project, You can see that in fact, it isn't really a photo. It's more like a map or a floor plan. So in the same way that the Conference Center class name was a bit misaligned with the actual functionality, this instance variable is also not quite named correctly. So we're going to change it. So we can go through those steps again. First, we need to select the text. Next, we need to bring up the refactoring window.

Now, as you can see here, that checkbox that I told you about before, the related KVC members checkbox, is now active. And it's active because you have selected an Objective-C class instance variable. So let's go ahead and check that. And so again, we're saying rename picture2, and we need a new name. In our case, we're going to call it floorplan.

All right, so we can go ahead and preview this. Wow, so, okay, so again we see that, hey, what's going on? There's a nib file in the results. So when we did the first transformation, only source code files showed up there. But now, a nib file shows up, so what's going on? Well, you've probably already seen this a lot in your own code, that whenever you change instance variable names in classes that you're working on, that doesn't change them in the nib files if those classes are referred to in any nib files.

So what you need to do is you need to find those nib files and either drag the header over again and reconnect everything up, or you type in a new name. You have to do this for every nib file. It's error-prone, it's tedious, and with refactoring, you don't have to do that anymore. So let's have a look at the room. that name file. Okay, as you can see here, we've gone ahead and made that change for you. We've changed the picture outlet to floor plan. And that outlet is still connected, everything will still work properly for your code. Yeah.

We think it's pretty neat too. But we're still not done. So if we go to the room.m file, Yep. You'll see that we are changing the instance variable picture to floor plan, but we're also changing a couple of other things in this file. We're also changing some method names.

In this case, the picture and set picture method names. So why are we doing that? We're doing that because we checked that related KVC members checkbox. Now, KVC stands for key value coding. This is a way to refer to the data in your class instead of directly by methods by using string-based keys and key paths.

So how this works is there are also methods that are related to those keys by various patterns. The patterns here are there's a getter and setter pattern. And so, again, the getter is very simple. It's just picture, and the setter is set picture. Now, there are plenty of other more complicated patterns for methods related to particular key names. They especially come up with too many relationships. We're not going to be showing those today.

But you can see that we have changed all of the places in this particular file. particular file that are related by a KPC. Right? So we can go ahead and commit these changes, apply these changes as well. All right. So we can go back to the demo machine. Actually, the slide machine.

All right. So the next set of transformations I'll be going over today have to do with class hierarchy. So what we've been doing so far is only changing individual classes. However, sometimes you also want to change a series of classes, classes that are related by class hierarchy or should be. And the classes that fit that bill in the get a room application are the escalator, elevator, and stairs classes.

Now, if we look at a snippet from a class model that was generated by the get a room project, we can see that, in fact, they all have exactly the same content. They all have a name, instance variable, and they all have a duration in seconds from floor to floor method. Now, again, this is all fine in your code. It will all work properly.

However, when someone comes in later to maintain the code, to fix bugs, or to add new features, what they're going to find is if they want to add new features to one of these classes, they have to add it to all three if they want to make them all work the same way. And it's very easy for the functionality of these classes to diverge slightly over time, for bugs to be introduced in one place and not in another place. It can get to be a real mess.

So the way you want to get around that is to take all of the shared code from these three classes and abstract it out to a single superclass. And that is what we're going to do for you right now. So if we can go back to the demo machine. So at this point, we're going to be doing a little bit of a demo. Let's go to the stairs class, stairs.h.

Okay, and again, we need to follow our steps. We're going to select the thing that we wish to transform, in our case, the stairs class. and bring up the refactoring window again. But in this case, we're not going to use the Rename transformation. We're going to switch to another one, which will be the CreateSuperclass transformation.

Now again, as before, there's a kind of a sentence going on here. Create superclass of stairs named a new name that you have to type in. In our case, we'll call that new class floor changer, I guess. That's not really a great name, but it's all I can come up with right now. However, if I come up with a really great name later, you can use the rename transformation to rename it to that better name. So we can go ahead and apply the changes here.

So as you can see, we've made changes to two files here. If we go to the .h file, you can see that we have created a new decoration for the floor changer class in the same file as the decoration of the stairs class. And if you go to the .m file, you can see that we've created a new definition of the floor changer class in the same file as the stairs definition.

So, okay, we can go ahead and apply that. As you can see, right now, the floor changer class is empty, and that's not what we want. So what we need to do now is move up some of the content from stairs to floor changer. Now, actually, the first thing we want to do here is a bit of housekeeping.

So what we need to do is we actually need to save these files before we continue. Now, why do we need to do that? This is because the refactoring functionality in Xcode relies on the Xcode project instance. So we need to save the index for some of its functionality.

And currently, the index requires that files be saved before their new contents are re-indexed for us to know about. So this means that because the floor changer is only in files which are unsaved, that means the indexer doesn't know about it, and we need the index to know about this new class. So we're going to go ahead and save these files, and then we can continue.

So now we're going to select the name instance variable, and again, bring up the refactoring window. And this time we're going to switch to the move up transformation. Now, as you can see here, again, there's a sentence, move up name to class for changer. But in this case, we already know everything we need to know to make the transformation work. And that means that you don't have to type anything new in yourself. All you need to do is click that preview button, which we will do now.

Now, one of the things you saw there very briefly was that for just a second, there were a lot of extra files in that list. And they were all system header files for the most part. So what happens is, when we come up with that initial list of files to look into to see if we need to make changes to these files, what we're doing is we're just very simply asking, who knows about something called name? And of course, name is a very common, well, name.

That means that there are a lot of false positives there. But we know enough to disallow these files very quickly from the results, and only narrow down to exactly the files and the classes that we actually want to change. In this case, the stairs class. So here we go. We'll hit the header here. Okay.

So here, as you can see... Okay, yeah, okay. All right, well, okay. So a little bit of a glitch there, but as you can see, what happened was we moved up the name declaration from stairs to floor changer. Very simple. So now we're going to do that again with the method. But as you can see, in the duration in seconds from floor to floor... Okay, so in this case, however, we have a method name which is actually broken out over several parameter names.

So we can't just select the entire name in one contiguous selection. But all we really need to do here is actually select a portion of the name, and as we bring up the refactoring window, and switch to move up, You'll see that in fact we have the entire name correctly listed there. We can operate on partial selections. That can be very useful to you. So, we're all set. We can hit the preview button here as well.

Now, if you look at these files again, in the .h file and the .m file, you can see that, again, we've moved things up from stairs to the four changer clouds. If we actually look at the .m file, Here it actually looks, here the method is long enough that actually the diffing engine that we're using treats that as something that's pinned and everything else as something that's moved around. But it still is all, all the changes have been made correctly, so we can go ahead and apply these changes.

So that is, oh actually, so we've done some of the work here for you. However, the transformations can't do all of the work. In fact, you still need to either delete or merge in yourself all of the methods from the other classes, escalator and elevator. And you also need to, by hand, connect those classes up to their new superclass, 4changer. So we can't do everything for you here, but we can automate some of the work, which again should make your life a lot easier. Okay, so go back to the swipe machine.

So we're going to go to the third transformation, which is encapsulate. Now what we've seen so far is that we've been working on the classes of the Getter Room application. But there are some places in this code where you decided when you were writing the code that it didn't need a full class.

Some of this logic was simple enough that all you really needed was a very simple structure to make that work, a C structure. One of these places was the TransitInfo structure. Now what this structure does is it's a place to add up all of the information from the individual steps of the transit, of the progress you need to make to get from one place to another, so that you can have it all aggregated together somewhere.

So, but what has happened then, again in version 2, in version 1 this was fine. In version 2, what you found is that you need to do more complex logic with this structure. You may need to calculate a variety of different routes according to certain criteria, like I want to be able to get the most exercise from my trip, so I want to go by the stairs as much as possible. Or I want a wheelchair accessible route, which would use only the elevator. So a simple structure is no longer required.

However, if you need to add all of this custom logic to your application everywhere where these structured fields are accessed directly, that's going to be a nightmare. So what you really need to do is abstract out that logic into one place, where then you can make these extra changes that you want to make. So, we're going to show you how to do that right now. So again, one more time, back to the demo machine.

Okay, so if we go to the transitinfo.h header file, we can see the decoration of our structure. Now, unfortunately, in this preview version of Xcode, we can't initiate a transformation from this particular header file because of a glitch that has to do with if structures are only referred to by a .m file. So we can't do that right now. But what we can do is we can go to one of the usages of the structure and initiate the transformation from there. So we'll go to the conference-center.m file.

And if you go to the very bottom-- We can see that the duration in seconds field is being used here. Now, it's being used in two ways. We are setting its value, and then we are also getting its value in order to calculate the new value to set it to. So, let's get started with the transformation. We select duration in seconds, bring up the refactoring window, and switch to the encapsulate transformation.

and here the sentence is: "Encapsulate duration in seconds, setter and getter." So in this case, because we're dealing with C structures instead of with instance variables in an Objective-C class, since there's no class, we can't actually make methods here. So what we're actually going to do is we're going to make C global functions for the setter and getters.

So if we go ahead and preview this-- OK, and we look at the .m file here. OK, this is a bit of a glitch here, but we can look at the comparison area at first. So as you can see, the places where we're setting the value of the field have been replaced by an invocation of the setter function, and the places where we're getting the value have been replaced by invocations of the getter function. We've also added the--

[Transcript missing]

Quit Xcode and Try Again. So that may happen sometimes in your own usage of the refactoring feature in Xcode. If this happens, just close the project and reopen it and try again, and it should work then.

Okay, there you can see the changes. If we go to the .h file, you can very quickly see the function decoration. Everything looks good, so we're going to apply those changes. Great. So back to the slide machine. Okay? So the fourth set of transformations I'm going to show you today have to do with Objective-C 2.0. And the two things that we can do for you are to modernize your loops and to convert to using properties in your code.

So for modernizing loops, as you can see from the code snippet here, we do two things. We erase all of the working variables that are no longer needed. And we also changed the loop statement to use the for-in syntax. Now, that for-in syntax can be optimized a little better by the compiler, so you get faster code, and you also get more streamlined code, because we've taken out a bit of extra work there.

So one thing to note about what we do here for you is we will only change loops which have to do with iterating over an NSSet or an NSMutableSet or an NSArray or an NSMutableArray. We will only convert things which go a very straightforward way through the array or set.

If you go backwards, if you do any other sort of special logic, what we do is we play it safe, because we want to have that guarantee that we are not going to change the meaning of your code. So if we see anything we can't do, we just leave it alone. But for the ones which are safe, we go ahead and translate this for you.

So for using properties, what we do is we add a property decoration to your classes that refers to existing instance variables in the class. Here we're adding a decoration for myString. And so these are very neat features in Objective-C 2.0. We're not going to have time to explain them here today, but go ahead and look at the documentation on Objective-C 2.0 yourselves, and you'll find out cool it is. So now we're going to demo this.

Now, there is a transformation for changing one loop at a time. And like the other transformations, it involves you selecting something and bringing up the window. But for these particular conversions, there's also another way to get going for your project. And that is by looking at the Edit menu and selecting the Convert to Objective-C 2.0 menu item.

Now, why is this a separate menu item? This is separate because, unlike the other times when you need a valid selection to get going, this is project-wide. As long as your project is open, as long as you're not indexing, this will be active. So we can see in this window that there are the two checkboxes for the two possibilities. We're going to go ahead and check both of those and get going with the preview.

Now, why is the UseProperties checkbox not checked by default? This is because UseProperties is a bit more experimental than the Modernize Loops functionality. There may be times when you apply this to your code where there are some places, unfortunately, where the additions that we're putting into your code don't compile. So you'll need to take those back out manually yourself, and then you'll be okay.

But we figured that the times where the additions are correct certainly outnumber the times where they aren't, so we think we're still saving you some effort there. But again, just so you know, it's a bit more experimental. So as you can see the results here, you can go to conferencecenter.m, the very first file. And you'll see that the first change that we made is to modernize a group. In this case, we've actually modernized a group which is using the NSEnumerator class.

So now if we go to the second file, you'll see that in this file, we've added a property. And in this case, this property decoration is also referring to a getter method as well. Again, there's a lot of neat functionality here, so I urge you to look at the documentation. Alright, so that looks good, so we're going to apply these changes as well. And we're done. Go back to slides. Go back to the slide machine.

So, in this very brief amount of time, I made a lot of changes to this project. What happens if I say, after I've done all these changes, "Oh no, I screwed up. I shouldn't have made all these changes. Maybe we're shipping tomorrow, maybe we're going in an entirely different direction.

I need to get my code back to the place that it was in before I started." Now, if I had made changes to individual files, that would be fine. I'd just hit Command-Z and I would undo those changes. But once you get into large-scale changes, hundreds of files even, that becomes a lot harder. You need to go to each file and hit Command-Z for each one of them.

And of course, for nibs, you don't even have that possibility, because when we make changes to nib files in refactoring, there's no undo stack. We're changing the nib files directly on disk. So you can't undo them. So Xcode can now make these mass changes for you. We need a way to roll those changes back.

And the way that we allow you to do that is a feature that was already announced this week on Monday: snapshots. Now, snapshots are not just for refactoring. They're for any time when you're making large-scale changes or checkpointing certain places in your code before you go and do something new.

and SnapShots are project-wide, unlike the stacks for individual text files. And unlike Undo, which happens sort of in the background silently, without you needing to do anything or see anything, we have UI to let you see what snapshots you've taken and what they look like. So I'm going to show that to you right now.

So one of the things that I pointed out before was how there was this snapshot checkbox in the UI for refactoring. And we checked that, and we checked that for all of the refactorings that we did. So what that means is we have now taken a snapshot for each point where we did a transformation. So we can have a look at that by bringing up the Snapshots window, which is available under the Edit menu.

So as you can see, there are now snapshots with names which should look familiar to you. Before Rename Conference Center, Before Move Up, we've given them these default names, which should be very easy for you to spot what's going on. So now, if we have a look at the very first snapshot that we took... You will see that we list all of the files that are different in the snapshot from the current state of your project. So we can have a look. Let's say, look at the room.h file.

So this file now has the aggregate of all the changes we've made to it in all of the refactorings we've done so far. We have changed the picture instance variable to floor plan, and we've also added a property decoration. So OK, so this looks good. So let's restore to this snapshot.

Now, as you can see, there's that little progress dialogue that shows up. And that dialogue showed up every time we did a transformation as well. And that dialogue shows that we are taking a snapshot. We're just restoring a snapshot, so what's going on? In fact, we actually take a new snapshot every time you restore from an old snapshot. So it's actually a little hard to describe. But in any case, what we do is we make it easier for you, every time you do something large-scale, to go back to the way it was before.

And what we're trying to do here is to make your coding a bit more fearless. You can make large-scale changes, they're going to be more safe than ever, and you know that you can always fall back to what we were doing before. So go back to the slides, please. And so that's it for the demos that I'm going to show you today. Now I will pass it back over to Robert, where he will talk about hints for using refactoring. Thank you.

Thank you. Okay. So at this point, you have seen or you have heard what refactoring is, which most of you actually knew what it was. I was very impressed. You've also seen how refactoring looks in Xcode, and you've seen how you would use it. What I'd like to do now is tell you a little about what issues you may encounter as you are actually using this, some of the things to keep in mind, some of the things to be aware of, and so on.

Now, before that, one of the things that hopefully all of you were thinking about as you saw this example is, that was nice, but that seemed like a relatively small example. I wonder how it works on my 250,000 lines of code. Actually, how many people asked that? Oh, I won't bring it up.

One of the fun things with refactoring is that, you know, you're actually doing things like parsing code, and so it takes a while. And so one of the big issues is, how is this going to work? I want to be able to use this on my code. I've got a project with, you know, 100,000 lines of code, 250,000 lines of code.

Can I actually use it? And the answer is yes. In general, the amount of time it takes to do a refactoring will be proportional to the number of files that we need to parse. So the kind of times I see on typical machines in my office is to parse a single file, it usually takes about 10 seconds.

Now, you may be saying, why does it take 10 seconds to parse a file? But the reason is that when you parse that first file, that's when we actually parse the system headers. And so when you're including Cocoa slash Cocoa.h, that means you're including 100,000 lines of headers at that point.

And that 10 seconds is from the point where you start bringing up the refactoring menu, not when you hit the preview button. If you have about 36... In about 26... In 20 seconds, we can parse about 36 files. In about a minute, we can parse 100 files. And so that should give you an idea of how long refactoring might take.

Now, this doesn't mean that if your project is 100 files, it's always going to take a minute. We only parse the files that we think are necessary. As Andrew mentioned, what we do is we actually look at the names that are used in a given file to find out which files might be interesting. And we only parse those particular files.

Now, a second thing that you did not see in the example-- well, actually, you did if you were very careful about spotting things-- was the idea of what happens when you have errors. All of the transformations we did didn't have any errors, but what happens when that does happen? Now, errors are really important, because part of the reason why you're going through all this work of doing refactoring is because you want the tools to tell you, hey, that's not a really good idea. You don't want to do that.

And so what the refactoring feature of Xcode will do depends on how bad the error is. So, for example, if it's a problem where we just simply say, we can't do this. For example, you're making changes where we're going to have to make a lot of changes inside macros, or, for example, on a rename like this one here, you're trying to rename a class called SKT Graphic and convert it to SKT Graphic View, which already exists. We need to tell you, no, we can't do this. And so what you'll see is you'll have a dialog box, a sheet come down, and it will actually say, no, we can't do it for this reason.

Now there's also errors that only occur on certain points where we can do the transformation, but there's a certain number of places where we can't. So this example here is from doing an encapsulate. So we were trying to hide a particular field of structure, the left call structure on the left. And what happens is that if you look really closely, that field isn't being read or written, it's being incremented.

And we can't do an increment with just a get. We can't do it with just a setter function. And so we have to say, hey, you need to look at this. We're not sure what to do. And so you as the programmer, when you get one of these, can decide, OK, do I want to make a third function that wraps this variable that's going to be an increment function? Do I want to get the value and then set it? Or do I just want to give up on this transformation because I realize this is a little more complex than I imagined? These are the two types of errors you're going to see and the two ways that you can recover from them.

And now a couple practical tips that Andrew brought up. Now, one of the things to keep in mind is that refactoring changes your source code. It changes a lot of your source code. It can change all the files in your project. And because your source code is your work product, because it is the thing of value, you want to be really careful with it. We can make insane amounts of changes to your source code, and you may find out after you've done several refactorings, either that the direction that the refactorings are going isn't correct, or that you're not doing the right thing.

Or that perhaps you're not going to be able to do the transformation you want because the last one can't actually be done correctly, or you realize at the end, no, that's not what I really wanted to do. You need to be able to save your source code so that you can back off in those cases, because that does happen in real refactoring.

And so in those cases, you need to make sure you're either saving the files off on the side, you're using the snapshot feature, you're using a source code control system. Whatever it is, please make sure to save your source code. We don't want to have changed your source code in ways that you don't want anymore.

Another thing to keep in mind is that part of the fun of refactoring, part of the thing that makes it so hard, is that we actually have to parse your source code. We have to act like a compiler to be able to understand it. And that's how refactoring is able to do the precise changes it needs to do.

And as a result, if your code doesn't parse, if it's not syntactically valid code, we aren't going to be able to do the analysis that we want. Now, we don't restrict you. We don't say, "Hey, you've got some warnings in your code. We're not going to let you refactor." But we're going to let you actually do the refactorings.

But if your code doesn't parse, we may not make all the changes that are appropriate. So if you know that your program is in an incomplete state and you do refactorings, be a little more careful about checking to make sure the changes are what were intended. For example, you might have cases where changes after a syntax error don't actually get made.

Okay, now this is also the first time that we've shipped refactoring with Xcode. So in the, in Leopard Preview, it's, it's our first version. There's going to be some issues to keep in mind. One of the caveats that you should keep in mind is that this only works for C and Objective-C right now. So as long as you're, so you can refactor to your heart's delight in code that's completely C, Objective-C. If you have Objective-C++ or C++ in your code, that means that you can't start refactoring from those files.

Now, you can actually have a case where you've got a class that's defined in an Objective-C file or in an Objective-C header that's used in the Objective-C++ files. You can start a refactoring like that. We're not going to tell you where those changes are. You're going to actually have, if there's changes in the Objective-C++ files, you're going to have to search them on your own using the good old-fashioned approaches of text search. And hopefully that will remind you of what life was like before refactoring.

But that's how it is. Another thing to keep in mind is that many people try to move to Objective-C++ or try to work with C++ by turning all their Objective-C files into Objective-C++ files, into .mm files. And if they do this, it has the advantage that you don't have to worry about when you're including C++ headers and when you're not. If you're doing that, you might actually need to back off a little and convert some of your files back to being straight Objective-C and only including C and Objective-C headers, so that you can actually refactor those.

Now, one of the things that makes C such an interesting language is we've got this cool thing called the C preprocessor. And the C preprocessor is fun because it's this own little language, and it has the ability to do arbitrary text substitution, it allows you to do conditional compilation, it allows you to do really clever things. And unfortunately, it makes life absolutely miserable for anybody who's doing source code analysis.

So for this first version of refactoring, the way that we're going to handle the preprocessor is that we are only going to handle the current build configuration. That is, you're building development or you're building deployment. Whatever would compile as part of that build configuration is what code we are willing to change. You can find your build configuration either from the project menu or in the figure you can see on the screen here up in your toolbar.

So for example, with the code you see here, we have a variable called dailyRentalRate. And if you decide to rename that, we're certainly going to change the occurrence on the first line and the last line. However, that occurrence in the middle is used inside of a if-deft code, inside conditional code, where we only compile that if the debug macro is set.

The only time we're going to change that occurrence that's in yellow is if the debug macro was set in the build configuration that you're currently using. So make sure that when you do refactoring, you're using the build configuration that's it's going to include the code that you most want to refactor.

Another caveat to keep in mind is that we're currently only going to work on single projects. So for example, if you have your application broken into multiple projects, you have one for your frameworks and one for your applications, we are only going to change things in one project. So for example, in this case here, if you want to rename the class foo, you're We're certainly going to change the two occurrences in the My Framework class, if that's where you're doing the refactoring from. However, we're not necessarily going to change the occurrence in the MyApp project.

So if you want to change the stuff in other projects, then you either need to go and do that by hand using regular text search and replace. Another thing you can try is doing this with refactoring. Now, we do have a little restriction on the transformations right now. In general, we try not to allow you to do refactorings if you're changing something where you don't own the declaration, where it's not defined in your current project. So for example, we don't let you rename NSArray because we can't change foundation.

And so for the example of Foo, if you tried to rename Foo in the MyApp project here, we wouldn't actually let you change it, because we'd say, hey, we don't know what Foo is, we don't know where it is, we can't change the name. If you run into this case, one workaround that you can do is you can actually create a temporary dummy definition somewhere in that project, rename it, then pull out the dummy definition.

Now, a couple other minor issues you should keep in mind. We don't allow you to actually rename-- if you decide to rename-- let's start again. Many Objective-C programmers like the idea to make sure that the name of the file that a class is defined in matches the class name. If you're one of those people, then you're going to find that when you rename a class, we don't necessarily rename the file name yet. So that's something to keep in mind. Another thing to keep in mind is that we don't rename comments.

So if you change the name of a declaration, you're going to have to go through, and if there's places in your comments where you want the name to be changed as well, go through and do those by hand. And finally, for this first version, we're not allowing you to do things like rename macros or protocols or categories or properties.

So what you've heard now is you've understood what refactoring is, you've seen how we do refactoring in Xcode, and you've heard some of the details about how to make refactoring work for your own programs when you're doing it. As you've heard, refactoring is a way to make global, structured, controlled changes to your program. And refactoring makes sure that the code should compile the same before as after, and it should have the same output behavior. It should run the same before as after.

And the refactoring feature allows you to have a better time doing maintenance and enhancement style changes to your source code. This is the first version. We hope to continue improving it. We also hope that this will be a base for other tools in Xcode that manipulate source code or do analysis. We're looking forward to hearing what you think of this.

We're looking forward to hearing what you like about the refactoring feature, what you don't like about the refactoring feature, what you'd like us to do. Please file bugs. Please send feedback. We would love to hear about it. And we hope you enjoy the feature. With that, I'd like to bring Matt Formica on to close the session.