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: wwdc2007-312
$eventId
ID of event: wwdc2007
$eventContentId
ID of session without event part: 312
$eventShortId
Shortened ID of event: wwdc07
$year
Year of session: 2007
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2007] [Session 312] Refactoring...

WWDC07 • Session 312

Refactoring in Xcode: Automatic Project-wide Code Changes

Developer Tools • 59:26

Xcode 3.0 introduces refactoring, an automatic way to apply project-wide changes to the structure of your source code and user interfaces. With demonstrations and practical use cases, you'll learn how Xcode's refactoring capability allows you to safely, effectively, and quickly restructure your code. You will see how refactoring makes it much easier to maintain your codebase, convert it to use Objective-C 2.0, and continue to add new features to your application.

Speakers: Andrew Pontious, Robert Bowdidge

Unlisted on Apple Developer site

Downloads from Apple

SD Video (172.6 MB)

Transcript

This transcript has potential transcription errors. We are working on an improved version.

Welcome to Refactoring in Xcode. My name is Andrew Pontious. The agenda for today consists of three parts. First, what is refactoring and why would you want to do it? Second, we'll go through the refactoring transformations in Xcode. The workflow, to do them. And third, we will give you some hints on how to make the best use on refactoring and give you some things to consider when using it.

For those of you were in this session last year, some of this will be review and some of this will be entirely new. But before we get to that agenda, some of you may be thinking project wide automatic code changes, I can do that already with project find and replace. So I'm going to give you an example that those goes beyond project find and replace.

Let's say you inherited a project from a co-worker, but that co-worker, a little bit of a bird brain. When he was coming up with names for all of the parts of his program, all he could think of was:

( Plays audio clip from Disney's Finding Nemo. Seagulls repeating mine, mine, mine )

He had a class named Mine, a protocol named Mine, a structure named Mine, a function named Mine, a method named Mine.

You get the picture. So, what we'd like to do is change the class named Mine to its real name. And we'll show that to you right now. Can we switch to the demo machine? Can we switch to the demo machine? Are we all set, oh ok thank you.

On our demo machine this morning, Yugi Yakomoto. Ah great.

( applause )

Return engagement from last year. Okay, so we bring up the project find window and we type in the word mine, click the find button. And you can see there are quite a few results there. So if we go to look at some of these results for example the ClassMineFunction.m file, quite a few results.

We can see that interspersed among the usages of the class named mine there are also uses of the method named mine, even a string named mine, all sorts of things. So if you wanted to replace just the class names, you'd have to pick out exactly which results you wanted and then click the replace button, do a build and make sure you had selected everything that you needed to select that you didn't select things you weren't supposed to select.

It'd be really messy, really tedious thing to do, it would be a minefield.

( laughter )

( applause )

Not my joke. Okay, so if instead we go to the Mine.h file and select the class name mine, the string, and bring up the refactoring window and we type in the new name we want in this case drawing engine, click the rename related files check box and click the preview button.

Pretty quick, huh? So if we look at the results here you can see that for that same file that we were looking at before what refactoring has done for you is picked out exactly the ones you wanted to change for you. It's left the methods, it's left the string.

Now if we look a little further we can see to the next file of Mine.h. We're actually renaming Mine.h and Mine.m to the new names. Now very common custom in Cocoa programming, when you name a file the same name as the class, you want those to stay in sync. And refactoring does that for you and in fact it even changes import statements that refer to those files. Great. We can click the apply button and that will make the changes in the files in your project.

But you see there's even more to it than that. Save this file here. If you go back to the project window you can see that in fact this project is under a source control management system. And you can see with the ClassMineFunction.m there's an m next to it, it's a little cut off because the font is so big, but that's telling us that we made those changes and the source control management system recognizes that.

But next to the new class names, there's an a. And what does that mean? That means that what refactoring has done is it's noticed that you're using a source control management system and instead of just changing the files on disk it's also changed them in your local repository for you.

So all you would need to do to upload these changes is do one commit and you're done. Pretty cool, huh?

( applause )

  • Okay. So we can switch back to the slide machine. Now that I've given you a taste of what refractory can do for you I will bring up Robert Bowdidge to get started with our agenda. Thanks.
  • Thank you Andrew.

Okay. So Andrew's given you an example of what refactoring looks like. What I would like to do is tell you what refactoring is and why you want to use it on your own projects. So if I was asked to define what refactoring is, this is what I'd say: Refactoring is a way to make global, structural, controlled changes to a program. The changes are global because they affect all your source code. When you go and you say I want to rename this declaration, it's changed in all the places it appears.

The changes are structural because they're affecting the design of your program. They're affecting what abstractions exist, what they're called, how they relate to each other. And the changes are controlled they're not just a set of edits, but they're actually trying to maintain certain properties about the code. Refactoring tries to make sure your code still compiles the same before as after. And that it has the same running behavior before as after.

Now why would you want to use refactoring and there's two good reasons to use it: the first one is it allows you to make your code more maintainable and more understandable. For example, let's imagine that we're writing a program to run a car rental agency. And one of the classes we're liable to have in that application is something called CarRental which has all the information about a specific rental experience.

And this is a fine name, I would've done this if I had written the program myself. However let's imagine that you decide to change the program and you decide to start allowing that you can rent trucks as well as cars. And the problem is now this name is inappropriate. That whenever you or anybody else on the team looks at the code they're going to say okay was this just on the cars or is this for everything.

And so what you'd like to be able to do is change that name with a rename refactoring so that the name is something more generic like rental record so you know that this is nor just car specific but it's for all the vehicles you're renting. Now the second reason you can use refactoring is it makes it easier to apply certain enhancements.

So for example, there's the case of partitioning a class so in the car rental example you'll probably have a class called car that has all the information about one of the cars being rented. And that car as you can see on the left hand side has some information that's probably generic like what the odometer reading is, when the car was last serviced and then that class is also going to have methods that are more specific about cars. How many seats does it have, does it have a radio, does it have a roof rack. And this is a perfectly fine way to structure the program. However let's imagine that you decide to start renting trucks now.

And the problem is that you'd like to be able to reuse some of that code in the car class, the generic stuff. And so you can use refactoring to break that class into two pieces so you now have a generic vehicle class like you see on the right that has all the generic behavior and the subclass of that which is the car class that has all the car behavior. And once you've done this, it's easy to make the truck class that inherits off a vehicle and you get all that code for free.

Another example of how refactoring can help enhance code is the idea of encapsulation. So let's imagine that you have a class called CarRental which represents the information about the rentals, and that class probably has an instance variable called dailyRate which represents the cost to rent that car for a day.

And that variable is probably accessed in a lot of places in your code. There's probably places where you're changing the value based on what the discount rate is for the person who's renting the car. There might be something that accesses the daily rate to print out the receipt.

And those are just going to access it directly That seems like a fine way to write the program. However what happens when you decide that, that daily rate shouldn't be just a variable set in the class but it should be accessing a database so that the rates are consistent.

And if you want to make that change now you have to go to every place where daily rate's referenced and you have to figure out okay how does this relate to the database example. You know how do I need to change my code so that it will work with the database.

And the problem is that's going to be a lot of work, you have to go to a bunch of places, it's easy to make mistakes and what you'd prefer to do is be able to convert the code to what you see on the right. Where you encapsulate all the references to daily rate so that their hidden behind a getter and setter. And once you've done that and all the various code that was accessing it now goes through these functions all you have to do is change those two functions. And now you can do whatever behavior you need to change how daily rate's represented.

Now we as programmers do this kind of stuff already, we already do renames, we already do refactoring, but the problem is that it's an error prone and tedious activity. And so every time that I do this sort of thing, I have to ask myself am I introducing subtle bugs into my code by doing this? And if you want to do refactoring correctly you need to follow some steps kind of like what we have here. So first of all you need to compile your code and run your unit test to make sure the program behaves the way you think it does.

Then you have to test some preconditions for that transformation. So for example, in the case of rename you need to make sure that every place where the declaration is used doesn't have something already defined with the name of the new name where you have conflicts. Or in the case of encapsulation you'd have to look at every place in the code and decide whether the variable that you're encapsulating can be changed into either a getter or setter call.

Once you've decided that the transformation is safe, now you actually need to go through the code from top to bottom and at every single place that you need to change you need to make exactly the right change in exactly the right place. And if you mess up you introduce subtle bugs into your code.

And then once you're done now you need to recompile the code to make sure it still compiles. And you need to run your unit test to make sure that it still behaves the same But why do we have to do that? We have tools that already understand our source code we have compilers, we have code completion in our editors.

Why can't I as the programmer decide what my design should look like so that I can tell my tools hey this is how I need this to be? And the tools can do all the nasty, dirty work to actually make that true, can make the changes, can prove the changes are safe.

And tools like this exist for other platforms and for other languages. But in general they haven't been available for Objective-C programmers. And so the key issue in this session is that Xcode 3.0 now has refactoring support. And more than just support it's been optimized for Apple's languages and Apple's environment. So Xcode 3.0 will allow you to change C and Objective-C source files in your projects, the transformations understand the new Objective-C 2.0 language features and in fact there's some transformations specific to Objective-C 2.0.

Xcode also understands that your source code isn't just your source files. That there's other things that affect the behavior of your code. For example, there's nib files which describe the user interface. And there's core data files which describe how it interacts with the database. And then you have key-value coding so that you can access fields of a class by name, by string. And most importantly we realize that your programs aren't just little small examples. You have real programs of hundreds of thousands of lines and so we've worked hard to make sure that this will work on hundreds of thousands of lines of code on large projects.

Now there's five kinds of refactoring transformations that Xcode 3.0 supports. And the operations are called transformations because they have the mathematical sense of converting something from one form to another in a very controlled way. The first kind of transformation is rename as you saw, that Andrew demonstrated. And the rename transformation will allow you to rename classes and functions and methods and categories and protocols and everything. The second kind of transformation that we have is an extract transformation. This allows you to select a block of code in your program and say I need this to be a new function.

And Xcode will automatically make the new function, put in the function call, choose what the parameters need to be and this gives you a way to make your code more understandable by breaking up large functions or it allows you to break code off into a new method so that you can actually reuse the behavior or change it.

The third kind of transformation that Xcode supports manipulates the class hierarchy. These are transformations for creating an abstract superclass. For moving instance variables up and down on the hierarchy. The fourth class are the encapsulate transformations. These allow you to wrap either a field of a structure or an instance variable in the class behind getter and setter methods.

And finally there's transformation specific to Objective-C 2.0 that allow you to automatically convert iterators of our NS arrays and NS sets into the new for in notation with Objective-C 2.0 that's both faster and easier to understand. And there's also a transformation that will convert instance variables in your code into properties so you can see what properties look like. Now to actually demonstrate these I'd like to bring Andrew Pontious back on stage to show you the features in Xcode.

( applause )

Thank you. Thank you Robert.

So I'm going to walk you through these transformations. For some of them I will be doing a demonstration for you, for some of them due to time considerations I will just be showing you some slides about them, some use cases. So we'll start with rename. Now to demonstrate rename for you and all of the other transformations that we'll be demonstrating for you today, we're going to use a program called Bricksmith. Bricksmith is a real world, open source, Cocoa based application. And what Bricksmith does for you is it lets you model creations made out of Lego bricks, so you can both see them and you can make little instructions for other people to use them as well.

Now one of the things that happens with open source projects is that you can get contributions from anywhere. You can get them from all over the world, from people that you don't know. So sometimes these contributions might be in the wrong format for the existing program. So we're going to look at a case where we need to change that. Can we switch to the demo machine? So as you can see Yugi is playing around a little bit with the Bricksmith user interface. There's a pretty neat spaceship there, spaceship creation, and he's putting a mini figure inside of the cockpit.

So pretty cool stuff. So now if we go back to the Bricksmith project, and we look through the files in it, you can see that many of the files in this project start with the prefix LDraw. So the files are of the form LDraw plus something. But there's one file, there's one set of files in the list where the pattern is LDrawn plus something.

And you can see that, that's for the class called LDrawnPart. So if we open up, save the header file, we're going to rename this file so that it matches all of the others. So we can bring up the refactoring window, there are several ways to bring up the refactoring window, you can use the main menu bar, edit menu which has a refactor menu item, you can use the contextual menu for the text selection that you've made, and you can also use a tool bar item that you can add to your Xcode window toolbar.

Now when you've selected something which represents something that can be refactored, the tool bar item, the menu item will all be enabled. If you select something which cannot be refactored, for example blank space or comment, those items will be disabled. So let's bring up the refactoring window. And we'll rename that class to LDrawPart and do the same thing we did the first time which is rename related files.

Preview that. So as you can see as the preview's going on there, you can see activity going on in the window. You can see what it's analyzing. Now if we look at the result in here, save the first file, you can see that we've changed the usage of the LDrawnPart class.

If we go down to the next file, you can see as before we've changed an import statement. But the most interesting thing about this demonstration occurs a little down further in the results area we are also changing instead of just text files we're changing a nib file as well. So let's go to that.

So you can see that the view that you see for nib files is a little different than the view you see for text files. For the nib file what refactoring does is it shows you the pieces, the parts of a nib that refactoring can change. These are broken up into categories, the first category is the classes category because you can rename classes, if you go down a little further, you can see that the second category is for object controllers.

Object controllers refer to classes and you can see that for each one of the object controllers here, it has the name of the class that it refers to in bold and there are eight of these in particular nib file and they all refer to the LDrawnPart class. So what refactoring does for you is it changes that reference to LDrawPart. That'll work so we can apply those changes. And we can switch back to the slide machine.

Okay. Now that I've shown you a little more about the rename transformation I'd like to walk you though the contents of the refactoring window. The refactoring window is made up of three areas. From top to bottom they are, the set up area, the results area and the comparison area.

The first area you will see when you bring up the refactoring window is the set up area, all the other areas start out blank. The set up area is where you set up your transformation. Type in what needs to be typed in, check whatever check boxes need to be checked.

Now another thing you'll see in the set up area, you can see right now, is there's a checkbox called snapshot. And that's actually using the snapshot feature that's new in Xcode 3.0 that was described in the Xcode 3.0 the new development workflow yesterday in this room. And we'll talk a little bit more about that in a minute.

Now if you don't want to do rename if you want to use a different transformation what you do is you bring up the transformation pop up button. Now for this particular example, this selection has no other relevant transformations. But we'll see later on in many cases, there will be several available for you for your selection.

Now when you click the preview button as you saw, the next area comes up, this is the results area. And when the preview's going on the file that is currently being analyzed will have a little progress spinner next to it. Files that have not yet been analyzed will be in a lighter grey color.

Now when the preview is done, most of the files will show checkboxes next to them which means changes from the preview. Another thing you may see and we will see in many of our examples today, is a new category called the errors and warnings category. Now why does refactoring show you this category? When refactoring is doing a transformation and it finds something which it needs some help with where it knows or it suspects that it can't complete the transformation, it will give you that information. For warnings it's when it isn't quite sure what to do. For example you're renaming something which is of type id and so it can't perform the same kind of class inspection, class analysis that it normally would because that's something that will only be known at runtime.

For errors it's something where refactoring knows that something's going wrong. And it may be something that you can fix, it may be something that you just have to take note of but this transformation as it is, it knows will not succeed. And we'll see some examples of that too.

Now if you select one of the files in the results area, the third area comes up and that is the comparison area. The comparison area should remind you of another application that comes with the Xcode tools that you've probably all used, file merge. Like file merge there are two subviews that shows two different states of a file, like file merge there's a strip in the middle which has arrows to show the direction of the changes whether the changes are being made or not.

But there's several things where it is unlike file merge as well. In file merge when you flip an arrow around so a change will not be made, there's a merge view at the bottom that shows you the results of your choices. But in the refactoring window the right hand view is the merge view. So you can see when we flip the arrow so a change isn't being made that shows up on the right hand side.

Another difference is that the results area and the comparison area are kept synchronized. So in this case above what we've done is we've turned off one change but two changes are still being made. So the checkbox changes to a mix state. If we flipped all the arrow around so that no changes are being made the checkbox would become unchecked and if you flip them all around again then it would become checked.

Now this doesn't need to be driven from the arrow side you can also check and uncheck the checkbox yourself and that will flip the arrows around so everything is kept in sync. And the last way that the comparison view is different is that you can actually put your cursor in the right hand side of this area and start typing.

As you can see we've typed a one at the end of the parameter name and what happens when you do this during a refactoring, is the icon for the file changes to a dirty state. So now that I've shown you that we can go on to the extract transformation. Let me switch back to the demo machine.

Okay so to show you extract we're going to use some functionality from Bricksmith that has to do with the mini figure. A cute little figure you can get in Lego bricks there. So as you can see the mini figure has a lot of different options that can be set for it.

It can have a different hat, different hair, different face, different arms and legs, different clothing, all sorts of things. So as you can imagine the method that brings all that together is pretty long and pretty complicated. So it switched back to that, that's the mini figure dialog controller classes method generate mini figure. Bring that up.

And as we scroll down through that you can see it's a pretty long method. Now the trouble with long methods like this is that they're hard to maintain and they're hard to augment. Because whenever you change something inside of that method, you have to make sure that you're not messing up functionality that exists anywhere above and below what you're changing.

So what you'd like to do is split this up into discreet self contained identifiable chunks of logic and you can do that through the extract transformation. So let's start pretty much right at the top and we'll select a piece of code and we'll bring up the refactoring window and we'll see what happens.

As you can see for the extract transformation unlike the other transformations that bit of analysis has to happen before it can even show you its initial set up, user interface and options. As you can see what happens when that analysis is done is refactoring shows you the signature of the method that it is going to extract. So let's preview that.

Have a look. Ooh. Yeah that's s a pretty long method signature isn't it? So what's going on here is refactoring has done all that analysis work that I told you about for you. What it's found is that all 15 of those local variables that we selected are being created in that chunk of code are all needed later on.

So what refactoring does is it creates out parameters for those local variables If they were variables that needed to come in to the chunk of code they would become input parameters. So what refactoring is actually telling us here is this particular chunk of code is not a good candidate for our goal. It's not to be self contained. So let's try again.

We can cancel out of this and select some code a little farther down. The head pieces section. We bring up the refactoring window again, a little bit more analysis going on. Now when we see the method for this piece of code it's much more reasonable. Why don't you preview that here. Bring that up.

You can see that for this method there are only three input parameters, the head, hat and neck pieces. So that makes some more sense to extract that out. But before we go ahead and apply that let's have a look at the other option that's available to you in extract which is instead of extracting a method, you can extract a function. Now why would you want to do that? The thing about functions is they are even more self contained than classes.

So preview this. And what we see here is when we attempt to extract a function from the same code there are more parameters. What's going on? What's going on is there's two methods that we saw before the new one and the old one. We're actually communicating, had context going back and forth that was hidden because it was the instance variables of the class.

So what we see here is some of those instance variables are actually needed by the functionality in the new method, new function, so that means that this is not a good candidate for making a function out of. What you want to do for functions is make functionality that can be used by more than one class that's very generic. So refactoring can do both those things for you as appropriate. For now we'll switch back to extracting a method and we'll call this method generate head pieces. And we can preview that again and apply it. And we're all done. Switch back to the slide machine.

So the third set of transformations I'm going to show you have to do with class hierarchy. Create superclass move up, move down. Now to show you these transformations, I have to ask a simple question. What's better than this? Modeling Lego bricks. How about modeling Lincoln Logs? You guys know what Lincoln Logs are right? Okay well, look it up.

So but what you actually want to do is you don't just want to model one or the other. You want to do both, you want to use the existing modeling infrastructure from Bricksmith and have it be able to do the same sorts of things, the same generic things for both. So let's try that out. Switch back to the demo machine.

Okay. So we'll go to the LDrawDocument class. Now just by it's name you can tell that it's actually fairly intimately tied with the Lego bricks implementation. Actually, LDraw is an open standard for Lego modeling programs. But there is still some functionality in this class that can be used for a generic modeling. So what we're going to do is we're going to create a superclass of this specific class and then move stuff up to it.

So we select the class name, bring up the refactoring window again. And this time we're going to switch from the rename transformation to the create superclass transformation. We'll type in a new name, the name will be modeling document since this is a generic modeling class and we can preview that now. Interesting thing going on here, we see our first warnings in a demonstration.

And the warnings say import statement may need to be changed. So let's look at the second warning. Now what you can see here, this particular import statement that refactoring has created for you, what refactoring does is it takes the immediate header file for a particular class and puts it in an import statement using quote notation. So straight, simple logic for you that's available in Xcode 3.0.

But we know in this particular case that NS document is part of the umbrella framework, Cocoa framework. So what you really want to do here is use the Cocoa framework umbrella header. So what you're just going to do here is type in that umbrella header import statement. So we're using that capability of doing manual edits.

Other than that, we're in good shape. So what you can see here is the first case of you working with refactoring, you intervening as necessary to complete a transformation that refactoring began for you. Refactoring did all the work that it could, you do the rest and you can apply the changes and know that everything will work. So you can go ahead and apply those changes.

Okay so now we've created our superclass. Let's move some stuff up to it. Go back to the LDrawDocument class and if you look at the very first instance variable there it's the tool bar controller instance variable. Now both the Lego document window and the brick I should mean and the log modeling window will need a tool bar.

So this is something that's generic enough to be moved up to the superclass. So we select the instance variable, we bring up the refactoring window again and we switch to move up and we're going to check the move up related methods check box as well and preview that.

Now there's no need for any other options for what class to move your instance variable up to because in Objective-C a single inheritance so there's only one superclass. As you can see in the errors and warnings section we're letting you know that we're also moving up a method.

So let's have a look at LDrawDocument.h. And you can see that in this existing subclass, we've removed the instance variable if we scroll down you can see we've also removed the method declaration. And if we look at the new class you can see that we've moved in that instance variable and the method declaration to it. Okay? We can apply those changes. And alright. So we can switch back tot the slide machine.

Okay so for move down I'm not going to show you a demonstration I'm just going to show you an example of it. So here we have the refactoring window for a move down transformation. And as you can see for a move down because you can have several subclasses we show all of them and that you choose which ones you want you can choose one or some or all, in this case we've chosen all of them. And you can see from the comparison view what's happened is for the subclass LDrawPart, we've moved down the instance variable is subclass to it.

Okay? So moving on to the encapsulate transformation. For encapsulate we will also not be showing you a demo today. But here's an example. So for that example we're going to go back to that mini figure dialog controller class that we looked at already and you can see in this class there are these 13 instance variables which all are Boolean, which all start with has, has something.

That's a little bit of repetition and maybe a little bit of wasted space that you might want to do something about. So what you could do is you could put these instance variables as the bit fields in a structure or you could put them into a core data entity so that would take care of all the storage and all you'd have to do is refer to them by keyword. But in any case in order to do any of that you need to be able to separate the back end implementation from all the colors. So you can start doing that by encapsulating the very first instance variable. And we can see how this works right here.

So here what's happened is, what refactoring has found out is that there's already a setter method of the name that we chose that's being used here. Now if we look at the results what refactoring has done is it's said oh here's a place where we're setting the variable to a value so we're going to replace that with the setHasHat method but in fact it's actually inside of the already existing setHasHat method. So that's no good. So you can go ahead and flip the arrow around to undo that particular change or you can use manual editing to fix it.

Either way once you fix those particular problems again the rest of the refactoring transformations work is done for you. And you can apply those changes. Okay so the last set of transformations I will be showing you today has to do with Objective-C 2.0. And as Robert said there is two things we can help you with to convert you towards Objective-C 2.0. We can help modernize your groups and we can help convert your instance variables to properties.

So for the first one as you can see here very simple loop. But even for this very simple loop there are some working instance variables that there's some working local variables, the indexing variable i, the cut variable ct, the working variable strItem, that kind of make this thing a little harder to read.

So after you've converted this to the new syntax it's much shorter, it's much more easily understood and it's also actually much faster. If you want more details about Objective-C 2.0 functionality I would encourage you to go to the Using Advanced Objective-C 2.0 Features sessions in this very room at 5 o'clock.

The second thing that refactoring does for you is it adds property declarations and definitions to your classes that match the existing instance variables of that class. So as you can see here we have a myString instance variable and what refactoring will do for you is add a myString property declaration to that class.

So let's have a look at how this works. Switch back to the demo machine. So while there is a modernized loop transformation that acts on your selection which you can try yourself, what we're going to do here, is do something a little more grand and we're going to use the project wide convert to Objective-C 2.0 transformation.

This transformation is available from the edit menu just as the refactor menu item is so we'll select that right now that brings up the refactoring window with two checkboxes for the two options that we've talked about, we're going to check both of those and click the preview button.

Now because this is project wide instead of just working on a certain sub selection of files it's going to take a little longer, that's the first thing, the other thing is it doesn't work on a selection so as you see we didn't have any selection set up there. That's because again it's for the entire project.

So let's have a look at the first file here and you can see that here what refactoring has done is it has added a property declaration and if you'll look at the second file we've added a property definition. In this case it's in the form of a synthesized directive.

And if we look at the third file we have modernized the loop. And for this particular loop we have eliminated the need for the counter local variable, okay? We can go ahead and apply these changes. Now refactoring can get you part of the way there. There are still other things you can do yourself you can switch to the dot notation for the usages of your properties you can remove unneeded getter setter methods in your class so there is more work to do but refactoring can definitely get you started and point you in the right direction. We can switch back to the demo machine, to the slide machine.

So we've shown you what happens when you refactor individual projects. But what happens when you want to rename something that's in more than one project? And in that case you have to perform some steps which we call cross-project transformations. Now Bricksmith the open source project actually has an example of this.

There are actually two Xcode projects that come with the Bricksmith project. One is the Bricksmith project itself and then there's a second one as well that we'll get to in a minute. So if you wanted to change the class AMSProgressPanel to something more generic, progress being a linear thing zero to whatever and you want to rename it to ActivityPanel so it's just a user doing something in the middle of something rather than progressing somewhere.

You can go ahead and do that in the Bricksmith project. But it turns out that, that particular class is actually declared and defined in a second project called AMSProgressBar. AMSProgressBar builds a framework that is then used by the Bricksmith application. But if you were to build things in this state it wouldn't work. So what do you do? It's actually very simple, you go ahead and rename the class in the second project as well.

Now order turns out to be important here. What you want to do is you want to start with usages of a class and then you want to progress on up to the place where the class is declared and defined. Why do you need to do this? You need to do this because refactoring has a safety feature built in which doesn't allow you to rename a class to the name of another existing class. So you can't rename your class NSObject for example.

So if you change the framework first refactoring would say well you're trying to change this other class to this name of something which already exists. So just very simple just go ahead and do all the usages first and you'll be fine. So let's go ahead and try this out. Switch back to the demo machine.

Okay so you can see the two projects here. So we're going to start with the Bricksmith project, the usage of the class and we're going to find that class that we talked about AMSProgressPanel, and once we find our usage of it if we can go ahead and select the class name and bring up the refactoring window. So we're going to go ahead and rename it to AMSActivityPanel and also again rename related files and you'll see why in a minute.

Okay so if we look at the results here first, we'll get to that error in a second, if we go to LDrawDocument go to the file, there we go, LDrawDocument file. So we've renamed the files in this particular project because you wanted to rename the import statements. But if we go on we can also see we're also changing usages, everything that we need to. But what's going on with that error at the top.

What's going on is that refactoring attempts to find all of the files that it can find that it can see and if any of them seem to be files that need to be changed, it tries to change them. But what it finds sometimes is that some of these files are files that it can't guarantee that it can change.

Files that are system header files for example or files that belong to another project for those it tells you know I can't do this. I'm going to let you know that I can't complete this transformation. But in our case we know that we can't complete it. We know that we have two steps to do so we're going to go ahead and apply these changes anyway.

Now we go to the second project and do the same thing. So you see that what Yugi's been doing is he's been saving some of these files as we go along. In some cases this actually turns out to be pretty necessary because the code sense index will only re-index files and re add that information that it knows about, when a file is saved not just when a file has been edited, and so in some cases you may need to save your files in between transformations to be sure that everything's updated properly. So here we're going to go ahead and do the transformation again.

And here you see the results have no error in them. That's because as far as this project is concerned it has everything it needs, the declaration and the definition of the class are all in files that it controls. We have a look. We can see again usages are getting changed, the file names themselves are getting changed here, import statements are getting changed, it's all good.

So we can apply these changes and then save the files so what we then need to do is rebuild both projects, rebuild the framework first and rebuild the application project and if the demo gods are with us, which okay give it another second here, okay maybe the demo gods aren't with us. But what you'll find, oh there we go.

Okay. Well definitely in your own projects when you try this what's going to happen is everything is going to get built correctly and you'll be able to run your application again. Everything will work. Okay so the demo gods weren't quite with us. But you trust me that, that works. So we switch back to the slide machine.

Okay. Now one thing that we have inadvertently showed you today is that when you are doing refactoring transformations sometimes there can be problems. So what do you do when you do a transformation where there was a problem or where you know that while I played around with this I actually don't want to keep it. And that's where the Xcode features snapshots comes in. As we saw already snapshots are turned on by the check box in the refactoring window and that checkbox is on by default. So you always have snapshot functionality unless you uncheck that checkbox yourself.

And what it does for you is it provides a way to go back. Try to do a kind of global undo. What happens with these changes that we're making some of the changes are text files and for those text files at least in theory you can click undo for each one of them and undo all of the changes made by refactoring.

But we also make changes to things like project files or nib files or core data files, actually core data files have an undo stack but nib files do not because they're in interface builder and not in Xcode proper. For those you can't undo yourself. So for those you want to use snapshots. What snapshots do is whenever you make a snapshot it archives away all of the files in your project for you.

Saves them under its own administration and so what happens is you get a window like this. So this particular window has some usages of snapshots very much like what we did today. Usually it says before rename, before extract, before create superclass. What happens is, is you said for example like we did with this last one when we renamed AMSProgressPanel if there was a problem with it you don't have to go ahead and get rid of all of your changes that you've made. Go back to say your source control management version.

You can just restore to the snapshot right before that last change. So we highly encourage you to do that for your projects. And so with that, I'm done. I will hand things back off to Robert Bowdidge who will talk about hints for refactoring.

( Applause )

Thanks.

Okay so you've just heard today what refactoring is. You've seen it in use in Xcode. What I would like to do in this last section is talk to you about what you should expect when you start using refactoring on your own projects. Now one question you might have is how long does refactoring take? And the short answer is that refactoring takes time proportional to the number of files that need to be analyzed, a.k.a. parsed or compiled, to figure out the changes and the number of files that need to be changed.

So here's some times on two machines that were in my office. When the change and I should also mention this is for a 200,000 line program that's part of Xcode. So if you do a rename that's only one file for example, renaming a local variable on my two machines the time went between three and eight seconds. If the change involved analyzing and changing three files it also took between three and eight seconds. And the reason why is because most of that time is spent parsing the system headers that are in every single source file that you include.

If the change involved 43 files, we saw times between 10 and 26 seconds. And if 123 files had to be analyzed and changed the transformation took between 25 and 43 seconds. C This was for rename and these times were comparable for all of the individual transformations. For the whole program transformation to convert to Objective-C 2.0, it's a little different because that transformation has to parse every single file in your project to figure out what to change. And so the time to do that is more proportional to the time it would take to compile your project. And so it's not surprising for large projects for the convert to Objective-C 2.0 to take on the order of minutes.

Now another issue is what will refactoring change in your source code. So in Xcode 3.0 the refactoring feature will change the C and Objective-C files in your project. It will also do some analysis to figure out whether changes are necessary in C++ or Objective-C++ files and will warn you if it thinks that there's changes needed there. But it will not currently change a C++ and Objective-C++ files Now refactoring does understand about comments in your code.

So if you for example extract a new function the comments in that new function body will move along with the new function. If you move an instance variable from a subclass to a superclass, the comment leading that instance variable will also move. However in Xcode 3.0 if you rename a declaration Xcode will not go looking through your comments in your source code looking for references to that declaration. So that's a case where you'll have to fall back on find and replace.

Xcode also understand about macros. And so if there are changes required in macro parameters Xcode will be able to do those as part of refactoring. It currently will not make the changes in macro bodies and it will warn you saying that this macro uses this variable that I need to change and you'll need to do that by hand. And as Andrew explained earlier if you have a change that goes across multiple projects you'll need to make that change in each project.

Now a second issue is what code actually gets analyzed as part of refactoring? So for example one of the great things about the C programming language is you have the preprocessor. And the preprocessor lets you declare conditional code. Code that's only compiled in certain situations. So you can take a block of coding you could say this code should only be compiled if I'm building for Intel. This code should only be compiled if I'm doing a debug build. This code should only be compiled if I'm building this particular feature that I want to test.

So the question is what happens with conditional code, what happens when we're doing the analysis and the rule for Xcode 3.0 is that whatever code would compile when you hit the build button is the same code that would be changed if you did refactoring at that point. So in this example here, we see that there's a variable called dailyRate that's used on all three lines. But the second line is hidden inside of a #ifdef that says that, that line is only compiled if the debug macro is set.

So if you went to rename dailyRate that variable would only be changed on all three lines if the debug macro is going to be set in the current build. You can see at the top that our current build setting for this project is the debug build setting which happens to set that macro and so all three occurrences would change if we renamed this at this point.

Another issue with analysis is the question of what happens when your code's incorrect. Xcodes refactoring will allow you to change files that don't compile. However while it will be able to make changes in those files it may not get things exactly right near the syntax error that's causing the compiler. So whenever possible try to make sure that your code compiles before you start doing refactoring.

And finally here are some strategies for the best results with refactoring. As Andrew pointed out refactoring changes your source code and as programmers, our work product is our source code we want to keep it. It's important to us. And to keep it correct. So one of the issues with refactoring is that refactoring is often an exploratory process.

You'll start doing a few transformations you'll look at your program and you'll say gee is this going in the direction I want? And you might find after several transformations you've either figured out that the entire task was inappropriate for some reason, isn't going to work out because of some detail you'd missed, or is actually incorrect.

And so you'd like to be able to go back in those cases. And so when you're using refactoring make sure you have a way to go back. Either use the snapshot feature that we built into Xcode for refactoring, save the files into your source code control system, save the files on the side, but make sure you have a way of going back because that's going to be a common practice.

The other thing to remember is that sometimes it's okay to do incorrect transformations. So for example let's imagine that you're renaming a declaration and it's used in 240 places. And you're able to change 200, or you ask Xcode to refactor that and it can make the changes in 239 and there's one case where it can't because of a conflicting name. There's nothing wrong with hitting the apply button then, having Xcode make all of the changes and then fixing that last case by hand.

Similarly there might be cases where you can actually use the analysis done by refactoring to do clever tricks that we didn't think of or that aren't really refactoring. So for example one way that you can actually change from one API to another is through rename. For example in Leopard the NSString's CString method is actually deprecated now.

And so if you, for example, need to change all of the CStrings to UTF-8 string one way you could do that is simply to rename the CString method to be UTF-8 string. You'll get a bunch of errors because refactoring will say that this isn't a safe transformation, it's not going to keep the code compiling it's not going to keep it running, because first of all it can't actually change the definition of the method because it's in a system header and it's write protected, and because it can't actually find the definition. But you can still hit apply, get the changes throughout your program and use some of the analysis of refactoring to find exactly where that method is actually used.

So what you've learned today is that Xcode 3.0 has support for refactoring. And specifically it allows you to modify and refactor your C and Objective-C files in your projects. You've learned that refactoring is a way to make global, structural, controlled changes to your program. And you've learned that refactoring is a great way to make your code more maintainable, more understandable or to make it easier to apply future enhancements. We hope you enjoy using this feature of Xcode we look forward to hearing your experiences, seeing the bugs that you find and please let us know what you think. With that I'd like to bring Matt Formica up to close the session. Thanks.

( Applause )

  • Thank you, I'd like to invite the Q and A team to come up at this point. If you have questions after this session or later in the week or in coming weeks, feel free to email me, I'm the Developer Tools Evangelist. I'd love to get your feedback on refactoring and how we can continue to enhance it.