Hour 19. Using Associative References and Fast Enumeration


What You’ll Learn in This Hour

Image Simulating adding variables to classes

Image Using fast enumeration

Image Using NSEnumerator instances with fast enumeration


Catching Up on Objective-C 2.0 Time-Saving Features

Objective-C has been around since the early 1980s (that was the time when most modern object-oriented programming languages or their direct ancestors were developed). It is set apart from many of the other languages by its goal, which was to build on C with the smallest amount of additional code. In part because of this design goal, major changes to the language have been relatively few when compared with some other languages.


Note: Comparing C++ and Objective-C Development Strategies

At an Apple Worldwide Developers Conference in the late 1990s, one of the engineers working on Objective-C compared it with C++ by recounting the story of a conference at which a number of new features were proposed for C++. A number of the new features were somewhat similar to one another, but, he said, every single one was added to the language. Coming from the Objective-C world, he considered that the wrong decision.


Objective-C 2.0 was released in 2007 along with OS X 10.5 (Leopard). The release included a new compiler and performance improvements to the runtime. Features such as declared properties, garbage collection, and others appeared for the first time in this release. By comparison, subsequent changes, such as Automatic Reference Counting, have not bumped Objective-C up to a new version number.

Among the Objective-C 2.0 features were the tools that are the topic of this hour: associative references and fast enumeration. Associative references fill a previously unmet need for extending classes, whereas fast enumeration makes programming faster and safer.

Extending Classes by Adding Instance Variables (Sort of)

In Hour 17, “Extending a Class with Protocols and Delegates,” and Hour 18, “Extending a Class with Categories and Extensions,” you saw how to add methods to classes. In the case of all of those tools, you can only add methods; there is no mechanism for adding instance variables. There are reasons why that feature is not implemented: Two of the most commonly cited are the fact that it is difficult to implement and the notion that being able to add instance variables to classes would lead to sloppy code. Regardless of where you stand on these issues, you cannot add instance variables to classes with any technique other than subclassing and adding the new instance variables to the subclass. See “Getting and Setting an Associative Reference” later in this chapter for another way of addressing this issue.

Associative references (introduced in Objective-C 2.0) simulate the process of adding new instance variables to objects. The process relies on key-value coding (KVC). The terminology is based on KVC: You create a reference from an object to a value where the value is half of the KVC structure.

GO TO ImageGetting Familiar with Property Lists” in Hour 15, “Organizing Data with Collections,” p. 207, to review key-value coding (KVC).

Adding what appear to be new instance variables is accomplished with a change to NSObject. Each NSObject instance has a set of references to associated objects. These references rely on KVC for their organization.

This implementation enables you to have any number of associated references for an NSObject instance. It also adds very little, if any, overhead because most NSObject instances have no references at all: The feature comes into play only for those objects that do have references.

Associative references let you add a pointer to any object—it need not be a class, but because it frequently is a class, the associative reference has the appearance of an added instance variable on the class. There are three functions that you use with associative references. You write the code, but the action happens at runtime and not during compile or build time.

The three functions are the following:

Image objc_setAssociatedObject—This function sets up the KVC between the object and the value to which it refers. The converse of this function is to call it again with the same object and a value of nil so that the previous reference is gone.

Image objc_getAssociatedObject—This function uses the key to retrieve the value you set up in objc_setAssociatedObject.

Image objc_removeAssociatedObjects—This function removes all of the values from the object.

You can hide the first two function calls in the custom setters of a declared property so that it truly appears that you are dealing with added instance variables. However, if you do so, add a comment so that it is clear to others (and yourself!) what is going on behind the scenes.

Adding an Associative Reference

Here is the function to call to add an associative reference:

void objc_setAssociatedObject(id object, void *key, id value,
  objc_AssociationPolicy policy)

object is the object to which you want to add the reference. If object happens to be a class, this is parallel to adding a new instance variable to the class. However, remember that this can be any NSObject instance.

key is the key. It is a unique string that is usually declared with code such as the following:

static char myKey;

value is a pointer to the associated reference object.

Finally, objc_AssociationPolicy policy uses pre-ARC terminology to define the choices you have for this argument:

OBJC_ASSOCIATION_ASSIGN
OBJC_ASSOCIATION_RETAIN_NONATOMIC
OBJC_ASSOCIATION_COPY_NONATOMIC
OBJC_ASSOCIATION_RETAIN
OBJC_ASSOCIATION_COPY

As with the options for declared properties, all setting actions are atomic unless otherwise specified. Also, remember that ASSIGN specifies a weak link, whereas RETAIN specifies a strong one. At the moment, there are no strong/weak values for these constants.

GO TO ImageManaging Reference Counts with Arc” in Hour 16, “Managing Memory and Runtime Objects,” p. 225, to review the settings for objc_AssociationPolicy.

With a strong link (retain), the associative reference remains valid even if the value for the associated reference is released. This is because the OBJC_ASSOCIATION_RETAIN retains (increments the reference count) the value object when the association is created.

Getting and Setting an Associative Reference

After you have an associative reference set up, retrieve it using this function:

id objc_getAssociatedObject(id object, void *key)

Removing an Associative Reference for a Key

You remove an associative reference by setting it to nil as in the following code:

objc_setAssociatedObject(self, myARKey, nil, OBJC_ASSOCIATION_RETAIN);

Remember to use the appropriate key. Do not worry about the value of the policy in this case. It has no effect when the value is nil.

Removing All Associative References from an Object

It is best to set each associative reference individually, but if you want to remove all of them from an object, you use the following function:

void objc_removeAssociatedObjects(id object)

Using Fast Enumeration

Whereas associative references provide a feature that did not exist before Objective-C 2.0, fast enumeration makes it much easier to do a very common chore that usually required coding (and recoding) the same control loop. All of the collection objects adopt the NSFastEnumeration protocol so you can use it with any of them.

GO TO Image Hour 15, “Organizing Data with Collections,” p. 205, for more on the collection objects (NSArray, NSSet, and NSDictionary) as well as on NSEnumerator.

In a nutshell, fast enumeration gives you an enumerated loop without having to write the control structure. The code is cleaner, and you avoid a potential problem. If you write your own loop, and if the underlying collection is modified while you are looping, you can get into trouble. With fast enumeration, if the underlying collection is modified during the fast enumeration process, an exception is raised so you can handle it.

There are two ways of using fast enumeration: with and without an NSEnumerator.

Using Fast Enumeration

This is the simplest way of using fast enumeration.

Using Fast Enumeration with an NSEnumerator

An NSEnumerator can let you enumerate an NSArray forward or backward. With NSDictionary collections, an NSEnumerator lets you enumerate over the values or the objects.

NSEnumerator instances are created by NSDictionary and NSArray on demand. For NSArray, there are two methods that return NSEnumerator objects:

– (NSEnumerator *)objectEnumerator
– (NSEnumerator *)reverseObjectEnumerator

For NSDictionary, two methods return NSEnumerator objects that enumerate through the keys and objects, respectively:

– (NSEnumerator *)keyEnumerator
– (NSEnumerator *)objectEnumerator

You use these methods as in the following code:

NSEnumerator *myEnumerator = [myArray reverseObjectEnumerator];

No matter which type of NSEnumerator you use, the basic process is the same. After you have your collection, create the NSEnumerator you want to use. The syntax in steps 3 and 5 changes to use the NSEnumerator instead of the collection:

for (id myEnumerationObject in myEnumerator) {
}

Summary

In this hour, you saw how to use associative references to simulate adding instance variables to classes and fast enumeration to make your collection loops safer and easier to read. Both features were added to Objective-C 2.0. This means that if you are maintaining old Objective-C code, you might be able to simplify it and sometimes speed it up by using these features. However, because this is happening at runtime, remember to ensure that your keys are unique so that there is no confusion about what is being added to the class.

Q&A

Q. What is the variable in the fast enumeration loop for?

A. It contains each enumerated object.

Q. What is the performance penalty for using fast enumeration?

A. None. It is faster than other techniques.

Workshop

Quiz

1. How can you use associative references as if they were properties?

2. In what way can fast enumeration make your code safer to run?

Quiz Answers

1. Declare a property and make it dynamic in your implementation file. Build your own getter and setter that use the associative reference functions behind the scenes.

2. Fast enumeration can raise an exception if the underlying collection changes as you are enumerating across it.

Activities

Take some existing code that uses traditional loops and convert it to fast enumeration. Look for the gnarliest and ugliest looping code so that you can see what the conversion to fast enumeration can give you. If you are new to a project, ask people who have worked on it for a while to show you the nightmare looping code. They’ll probably know just where to look.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.182.107