Loop modernization only transforms while loops that meet these criteria: The while loop iterates through the objects in a collection using an NSEnumerator object. Uses the message [enumerator nextObject] to obtain each object. Starts with the fi rst object in the collection. Processes each object, in order, without skipping objects (this does not preclude exiting the loop early using a break statement). The second and third points mean that you can ’ t use refactoring to modernize a loop that uses - reverseObjectEnumerator . An often - overlooked feature of fast enumeration is that the modern NSEnumerator class also conforms to NSFastEnumeration. The implication is that any enumerator object can be treated as the collection object in a fast enumeration statement. Consider the following code: NSEnumerator* e = [collection reverseObjectEnumerator]; for ( id obj in e ) { } The enumerator acts as a proxy for its collection. The statement loops through the objects returned by the enumerator. This is particularly useful with enumerators that return a subset of a collection or process objects in a different order, like - [NSDictionary keysSortedByValueUsingSelector:] . It should be noted that this trick does not improve the performance of your application; a fast enumeration using an enumerator object isn ’ t any faster than using the enumerator object directly. In other words, you don ’ t get the “ fast ” part of fast enumeration; you just get the convenience of a concise syntax. UPDATING A PROJECT TO OBJECTIVE - C 2.0 The Edit ➪ Convert to Objective - C 2.0 command is specifi cally designed to ease that transition of legacy projects to Objective - C 2.0. It, optionally, performs two transitions on every source fi le in your project: Modernize Loops Convert instance variables to properties To update your entire project to Objective - C 2.0, follow these steps: 1. Choose the Edit ➪ Convert to Objective - C 2.0 command. 2. Select either, or both, of the Modernize loops or Use properties transformations. The modernize loops transformation is the same one described in the previous section, just applied globally to every Objective - C source fi le in your project. ➤ ➤ ➤ ➤ ➤ ➤ Updating a Project to Objective - C 2.0 ❘ 197 c10.indd 197c10.indd 197 1/21/10 3:20:41 PM1/21/10 3:20:41 PM Download at getcoolebook.com 198 ❘ CHAPTER 10 REFACTORING The use properties transformation converts simple instance variables to Objective - C 2.0 properties. Is does this by Adding a @property directive for each instance variable. Adding a @synthesize directive for each instance variable that does not already have KVC - compliant getter and setter methods with the same name. REFACTORING PITFALLS As mentioned at the beginning of the chapter, any change to an application — no matter how seemingly inconsequential — can occasionally have profound effects on its behavior. Although the refactoring logic is quite intelligent, it isn ’ t omniscient. It makes mechanical replacements that are reasonably expected to be functionally equivalent, but may not be. Many of these pitfalls can be avoided by refactoring your application defensively and with the assumption that any transformation could introduce a bug. Try to follow these best practices when refactoring: 1. Your project should build successfully and function correctly before beginning any refactoring. 2. Make use of the refactoring preview; diligently review each proposed change before com- mitting the transformation. Disable any changes that aren ’ t appropriate. 3. Always make a project snapshot. They are easy to discard later. 4. After the transformation, make sure the project still builds. Fix any compilation errors that appear. 5. If you have any unit tests defi ned, run them. Each transformation carries some specifi c pitfalls. The next few sections highlight common issues that you ’ re likely to encounter when refactoring. Rename Renaming a method only renames the occurrences of that method defi ned by a single class. If that method overrides an inherited method, the inherited method in the superclass is not renamed. If you need to rename an inherited method, you must rename the same method in all other classes. Conversely, renaming a method could change it from a unique method to one that now overrides a method in its superclass. Encapsulate Encapsulate only generates getter and setter methods. It does not produce @property or @synthesize directives. If you ’ re using Objective - C 2.0, just write @property or @synthesize directives instead of trying to use encapsulate. ➤ ➤ ➤ ➤ ➤ c10.indd 198c10.indd 198 1/21/10 3:20:46 PM1/21/10 3:20:46 PM Download at getcoolebook.com The getter and setter methods assume that you are using traditional managed memory, not garbage collection. Though this will work in a garbage collection environment, it still generates more code than is optimal. Again, @property or @synthesize directives are a better solution. The @synthesize directive automatically adapts to your chosen memory management model. Existing memory management is overlooked, which can create over - retain and over - release issues. For example, encapsulating the ivar variable will replace the two statements [ivar release]; ivar=nil; with the statements [[self ivar] release]; [self setIvar:nil]; . This results in ivar being released twice: once in the code and again in the setter. The visibility of the original instance variable remains unchanged. If it were @public before the transformation, it ’ s still public afterwards. After the transformation, change the visibil- ity of the instance variable to @protected or @private . The setter method generated is not thread - safe. Move Up Moving an instance variable to its superclass does not migrate its @property or @synthesize directives. The visibility of a variable is not retained. A @private variable could become @protected or @public . If you migrate related methods, you could migrate methods that now refer to properties defi ned only in the subclass. This requires either migrating those other properties as well, or redesigning the methods. The refactoring tool warns you if the superclass already has a variable or method with the same name, but it won ’ t prevent you from applying the transformation. The result will be a class with duplicate methods. You can only migrate a variable or method to its immediate superclass. Move Down Moving an instance variable to one of its subclasses does not migrate its @property or @synthesize directives. The visibility of a variable is not retained. A @private variable could become @protected or @public . The refactoring tool warns you if any of the subclasses already have a variable or method with the same name, but it won ’ t prevent you from applying the transformation. The result will be classes with duplicate methods. You can only migrate a variable or method to one or more of its immediate subclasses. ➤ ➤ ➤ ➤ ➤ ➤ ➤ ➤ ➤ ➤ ➤ ➤ ➤ Refactoring Pitfalls ❘ 199 c10.indd 199c10.indd 199 1/21/10 3:20:47 PM1/21/10 3:20:47 PM Download at getcoolebook.com 200 ❘ CHAPTER 10 REFACTORING Modernize Loop The modernize loop transformation is generally safe, because it simply refuses to be applied unless the control loop meets its rather strict prerequisites. It is still possible, however, to trick the refactoring tool into transforming a loop that contains abnormal behavior hidden in preprocessor macros. Use Properties The use properties transformation may produce @property directives whose attributes (that is, assign , retain , copy , nonatomic ) do not agree with the implementation of the existing getter or setter methods. Review each new @property directive and confi rm that your getter and setter fulfi ll the property contract, correcting one or the other as appropriate. The transformation will not create @synthesize directives for properties that already have KVC getter and setter methods, even if the getter and setter methods use generic patterns. You may want to delete simplistic getter and setter methods before converting your project and let the transformation insert the more modern @synthesize directives. Unlike the encapsulate transformation, use properties will not replace direct references to instance variables ( ivar= ) with property “ dot ” syntax ( self.ivar= ) or getter and setter messages ( [self setIvar: ] ). You will need to search your code for direct instance variable references and determine which should be replaced with property accessors. No attempt is made to reduce the visibility of the existing instance variables. After the transformation, consider making the instance variables @private . This will also help identify direct references to the variable outside the class. SUMMARY Changing the structure of your application isn ’ t always as simple as moving a defi nition or performing a global search and replace. The refactoring tool makes short work of many common code changes, quickly and as intelligently as possible. This doesn ’ t relieve you of the need to confi rm the veracity of those changes, but it does relieve you of much of the tedium involved in making them. Before every transformation, the refactoring tool gives you the option of making a snapshot of your project. The next chapter explains what snapshots are and how to use them in other circumstances. ➤ ➤ ➤ ➤ ➤ c10.indd 200c10.indd 200 1/21/10 3:20:48 PM1/21/10 3:20:48 PM Download at getcoolebook.com Snapshots WHAT'S IN THIS CHAPTER? Taking snapshots of your entire project Reviewing snapshots and comparing them with your working fi les Managing and restoring snapshots If you ’ re like me, you hate to throw away code. Most code, especially the stuff that works, is a hard - won commodity that represents time, effort, expertise, and creativity. The best way to improve your code, however, is to experiment with new approaches — which often means abandoning or discarding the code you ’ ve already written. Making copies of your source fi les or saving snippets of old code as comments is both awkward and time consuming. Enter snapshots. Snapshots let you have it both ways: you can keep your existing code while simultaneously replacing it with new, experimental code. In simple terms, a snapshot is an archived copy of the source fi les in your project. It quickly preserves their current state so you are free to make changes to your project — even radical ones — secure in the knowledge that you can always revert back to the saved version at any time. You can make as many snapshots as you like. You can easily see what changes you ’ ve made, as shown in Figure 11 - 1, by comparing your working project fi les to those in a snapshot. You can revert your changes to any previous snapshot on a change - by - change basis, by fi le, or perform a wholesale restore of the entire project. ➤ ➤ ➤ 11 c11.indd 201c11.indd 201 1/21/10 3:23:41 PM1/21/10 3:23:41 PM Download at getcoolebook.com 202 ❘ CHAPTER 11 SNAPSHOTS Snapshots encourage experimentation by making it easy to save and contrast existing code with new code. There ’ s very little risk in rewriting a collection loop to improve performance when Xcode will preserve the previous implementation. Take a snapshot, rewrite the loop, and test its performance. Is it an improvement? Keep the new code. Not an improvement? Restore the saved snapshot and move on or try a different approach. It ’ s that simple. Snapshots resemble a source control system, but shouldn ’ t be treated as one. Each snapshot is (conceptually) a complete and independent copy of your entire project. Snapshots are not deltas, so they won ’ t preserve a history or audit trail of your changes — although you can compare two snapshots. Snapshots are cached on your local fi lesystem; they cannot be transferred to another user, nor is any snapshot information preserved in the project itself. Snapshots are intended to be a spontaneous method of marking your progress. Snapshots can be used independently of, or in addition to, the source control management features. See the “ Source Control vs. Snapshots ” section of Chapter 21 about issues that can arise when mixing snapshots and source control operations. TAKING SNAPSHOTS You have four ways of taking a snapshot: Choose the File ➪ Make Snapshot (Control+Command+S) command Add a snapshot from the snapshot window Take a snapshot via the refactoring tool Restore a snapshot ➤ ➤ ➤ ➤ FIGURE 11 - 1 c11.indd 202c11.indd 202 1/21/10 3:23:44 PM1/21/10 3:23:44 PM Download at getcoolebook.com Snapshots occur immediately; there are no dialogs or other overt indications that a snapshot was taken. Each snapshot has a name and an optional description; but when initially added all snapshots are given a generic name that describes how the snapshot was made (menu command, snapshot window, refactor transformation, or restore) and the date. If you want to make a snapshot memorable, rename it or add a comment to the freshly created snapshot in the snapshot window (File ➪ Snapshots). When Xcode takes a snapshot, it preserves the project source fi les contained within your project folder. If you have added source items to your project whose fi les are located outside of your project folder, those fi les will not be included in the snapshot. If you want resources outside of your project folder (like other projects) to be included in your snapshots, consider relocating your project’s root folder (see Chapter 21) or use the snapshot feature of the organizer (see Chapter 22). The minimal user interaction is intended to make snapshots a seamless part of your development workfl ow, not another interruption to it. On modern computer systems, snapshots are fast and inexpensive. Get into the habit of taking a snapshot before starting any atomic change to your project. Taking snapshots should become second nature, just as many developers save their fi les at strategic times. You will want to visit your snapshots occasionally to dispose of old and intermediate snapshots. MANAGING SNAPSHOTS Snapshots are managed in the snapshot window (File ➪ Snapshots). The snapshot window is typically collapsed and shows only the summary of snapshots, as shown in Figure 11 - 2. Click the Show Files button in the toolbar to display change information that was shown in Figure 11 - 1. The snapshot window is the central interface for browsing, commenting, examining, comparing, and deleting your snapshots. These actions are explained in the following sections. Commenting on Snapshots Snapshots are initially named by the method they were taken (via the main menu, before a refactoring, and so on) FIGURE 11-2 Managing Snapshots ❘ 203 c11.indd 203c11.indd 203 1/21/10 3:23:44 PM1/21/10 3:23:44 PM Download at getcoolebook.com . before a refactoring, and so on) FIGURE 11-2 Managing Snapshots ❘ 2 03 c11.indd 203c11.indd 2 03 1/21/10 3: 23: 44 PM1/21/10 3: 23: 44 PM Download at getcoolebook.com . refactoring tool Restore a snapshot ➤ ➤ ➤ ➤ FIGURE 11 - 1 c11.indd 202c11.indd 202 1/21/10 3: 23: 44 PM1/21/10 3: 23: 44 PM Download at getcoolebook.com Snapshots occur immediately; there are no dialogs. a wholesale restore of the entire project. ➤ ➤ ➤ 11 c11.indd 201c11.indd 201 1/21/10 3: 23: 41 PM1/21/10 3: 23: 41 PM Download at getcoolebook.com 202 ❘ CHAPTER 11 SNAPSHOTS Snapshots encourage