Chapter 22. Proxy

It's a pretty common marketing strategy that lures people to buy something by offering a trial period without any obligation to pay for it. Sometimes even a 30-day money-back guarantee is not as powerful as a use-it-for-"free" offer. It works particularly well on some expensive items or online subscription services.

We know there are a lot of online dating sites that provide subscriptions to singles who want to find their partners. A lot of them are quite expensive—let's say $100 for three months of unlimited searching and communication with other members. It's expensive, and there is no guarantee that you will definitely find your perfect match after you pay $100 within three months. So a lot of dating sites provide free searches, flirting, or other limited features but not actual communication with other members. This approach is more acceptable than shelling out $100 upfront and hoping for the best. If you really like the service and you get a lot of flirts and messages from other members (both paying and non-paying), then you may feel more comfortable about shelling out that $100 to dive into the game.

So you may ask, "What does this have to do with the Proxy pattern?" One common usage of a proxy is to act as a stand-in, lightweight object that allows clients to access some information or features that are cheap to prepare in the first place. Until the moment when it's worth it or necessary to get the real "deal," the proxy will go ahead and prepare the real, expensive resources for the clients. So a proxy offers you a trial membership at the beginning; when you are ready to pay for the real, expensive membership, the proxy will open that gate for you to access more features available only for paid members.

A design pattern that is elaborated from the idea is called the Proxy pattern. In this chapter, we will discuss its concepts and key features. We will also design and implement a virtual image proxy that displays a placeholder image on the screen while loading a real one in the background.

What Is the Proxy Pattern?

There are several types of proxies:

  • A remote proxy provides a local representative for an object in a different address space or in the network.

  • A virtual proxy creates heavy-weighted objects on demand.

  • A protection proxy controls access to the original object based on different access rights.

  • A smart-reference proxy counts the number of references to the real object for memory management. It can also be used for locking the real object so no other objects can change it.

Note

THE PROXY PATTERN: Provides a surrogate or placeholder for another object to control access to it.[20]

In general, a proxy acts as a surrogate or placeholder that controls access to another object, objects that are remote, expensive to create, or in need of securing. We can't cover every type of proxy here, so we will focus only on the virtual proxy.

The idea is to have a proxy that basically behaves the same as the real subject. Clients can use the proxy transparently without knowing that what they are dealing with is just a proxy and not the real thing. So when clients request some features that are expensive to create, the proxy will forward the request to the real object and get the features prepared and returned to the clients. The clients have no idea what's going on behind the scenes. Both a proxy and a real subject share the same behavior that clients expect. A class diagram that illustrates this idea is shown in Figure 22-1.

A class diagram of the Proxy pattern

Figure 22-1. A class diagram of the Proxy pattern

When a client sends a "request" message to a Proxy object, the Proxy object will in turn forward the same message to a RealSubject object that is sitting in the Proxy object. The RealSubject will carry out any of the real operations to fulfill the request for the client indirectly.

At runtime, you can imagine a scenario of a client who owns a reference to a subject as an abstract type. A reference to it is in fact a Proxy object. The Proxy object itself has a reference to a RealSubject instance that will perform any heavy-duty jobs later if requested. That scenario at runtime is illustrated in Figure 22-2.

A possible object structure of the Proxy pattern at runtime

Figure 22-2. A possible object structure of the Proxy pattern at runtime

When Would You Use the Proxy Pattern?

You'd naturally think about using the pattern when

  • You need a remote proxy that provides a local representative for an object in a different address space or in the network.

  • You need a virtual proxy to create heavy-weighted objects on demand. We will implement that kind of proxy in a code example later in this chapter.

  • You need a protection proxy to control access to the original object based on different access rights.

  • You need a smart-reference proxy for counting the number of references to the real object for memory management. It can also be used for locking the real object so no other objects can change it.

Lazy-Loading an Image with a Virtual Proxy

In Chapter 2, we have defined some requirements for the TouchPainter app. One of them was to have a ThumbnailViewController allow the user to browse through all previously saved scribbles in the file system. But loading all of them is not practical, especially when the memory on the device is very limited. Even if we can load all the thumbnail images in the memory at the time when the user enters the view, the performance may suffer if there is very little memory left for the rest of the application.

In the original design discussed in Chapter 2, scribbles are presented as little thumbnails lined up on the screen row by row. Each row is an instance of a customized UITableViewCell that contains multiple thumbnail placeholders that will present a corresponding thumbnail image of a scribble. Before any actual thumbnail image of a scribble is loaded, a thumbnail placeholder should present a placeholder image that is shared among other thumbnail placeholders on the same viewable area of the view. The framework at runtime will manage UITableViewCell objects. Cells that are viewable (outside of the viewable area of the screen) will be destroyed, while cells that are entering the viewable area will be created or initialized and reused with other off-screen cell resources.

When a real thumbnail image is completely loaded, the original placeholder image will be replaced by the real image. The process continues until all onscreen thumbnail images are completely loaded. The image on the left in Figure 22-3 shows a screenshot of the thumbnail view when it is first being loaded with only placeholder images. The image on the right in Figure 22-3 shows the same view with partially loaded scribble thumbnail images.

A scribble thumbnail view with only placeholder images vs. the same view with partially loaded scribble thumbnail images

Figure 22-3. A scribble thumbnail view with only placeholder images vs. the same view with partially loaded scribble thumbnail images

This type of user interface is used in some iOS applications created by Apple to load thumbnail images while the user is scrolling the view for a whole collection of them. This provides a smooth operation and response to the user while individual small thumbnails are being loaded in the background.

Designing and Implementing a Scribble Thumbnail View

The key elements to make that kind of "magical" thumbnail view are a placeholder image proxy and a mechanism for loading a real image in the background, so that the whole process looks smooth. We can lay out our basic proxy design in a class diagram illustrated in Figure 22-4.

A class diagram of ScribbleThumbnail proxy structure

Figure 22-4. A class diagram of ScribbleThumbnail proxy structure

The basic structure of the class diagram for ScribbleThumbnailViewImageProxy and a real image object represented by UIImage captures the essence of the original Proxy pattern. They are customized to fit what we needed for the TouchPainter app. Both the ScribbleThumbnailViewImageProxy and UIImage implement pretty much the same draw* interface to draw things on a UIView object. ScribbleThumbnailView itself is a subclass of UIView, so subclasses can be used with our UITableViewCell that can display them. The complete implementation of the whole user interface involves many other parts. For this example, we will go over only some essential elements that can highlight the concepts of the Proxy pattern. You can check out a copy of the example code for this chapter for the detailed implementation of each piece.

The idea of the image forward-loading mechanism in our ScribbleThumbnailViewImageProxy is that it will load a real image if the real one is not yet loaded and then present it on the screen. But that is just a bottom-line idea. It takes some more tricky moves to make it a working solution for our TouchPainter app. We will get to that later when we discuss the implementation part of the code example.

So far, we've got the big picture of what's going to happen with our proxy design for thumbnails displayed on a view. To visualize what we should expect at runtime with a proxy structure, let's take a look at a diagram that can illustrate that in Figure 22-5.

A ScribbleThumbnailViewImageProxy object and a UIImage object at runtime

Figure 22-5. A ScribbleThumbnailViewImageProxy object and a UIImage object at runtime

At runtime, aClient is accessing an instance of ScribbleThumbnailView's subclass, in this case, ScribbleThumbnailViewImageProxy. An instance of UIImage is not created until it's requested by aClient. So the real image data is always on disk until the ScribbleThumbnailViewImageProxy object is asked to load it. If the actual loading is never asked to trigger, then only the ScribbleThumbnailViewImageProxy object stays in memory. We can use the similar lazy-loading approach for loading any other expensive resources that need to present on a UIView.

Implementing the ScribbleThumbnailView Class

Listing 22-1 shows a class declaration of ScribbleThumbnailView.

Example 22-1. A Class Declaration of ScribbleThumbnailView in ScribbleThumbnailView.h

@interface ScribbleThumbnailView : UIView
{
  @protected
  NSString *imagePath_;
}

@property (nonatomic, readonly) UIImage *image;
@property (nonatomic, copy) NSString *imagePath;

@end

The only thing that concerns us about the behavior of ScribbleThumbnailView in general is the actual path with which we can load and return a real image. As an abstract base class, ScribbleThumbnailView maintains the abstract image and imagePath properties. These properties are essential for the whole virtual proxy operations we are going to see later. There is not much going on in the definition of the ScribbleThumbnailView implementation except that there are some property-related directives, as shown in Listing 22-2.

Example 22-2. An Implementation of ScribbleThumbnailView in ScribbleThumbnailView.m

#import "ScribbleThumbnailView.h"


@implementation ScribbleThumbnailView

@dynamic image;
@synthesize imagePath=imagePath_;

@end

Implementing the ScribbleThumbnailViewImageProxy Class

Everything seems straightforward so far. Now we are going to get to the trickiest part of our proxy design—implementing ScribbleThumbnailViewImageProxy. Its class declaration is shown in Listing 22-3.

Example 22-3. A Class Declaration of ScribbleThumbnailViewImageProxy in ScribbleThumbnailViewImageProxy.h

#import "ScribbleThumbnailView.h"

@interface ScribbleThumbnailViewImageProxy : ScribbleThumbnailView
{
  @private
  UIImage *realImage_;
  BOOL loadingThreadHasLaunched_;
}

@property (nonatomic, readonly) UIImage *image;

@end

realImage_ is used for holding a real image reference after it is loaded. A private BOOL member variable, loadingThreadHasLaunched_, will be used later for forwarding a loading process for a real image. The implementation of ScribbleThumbnailViewImageProxy is relatively long. So go get a cup of coffee before we start rolling it.

If you are ready, let's get started and take a look at its implementation in Listing 22-4.

Example 22-4. An Implementation of ScribbleThumbnailViewImageProxy in ScribbleThumbnailViewImageProxy.m

#import "ScribbleThumbnailViewImageProxy.h"

// A private category for a forward loading thread
@interface ScribbleThumbnailViewImageProxy ()

- (void) forwardImageLoadingThread;
@end



@implementation ScribbleThumbnailViewImageProxy

@dynamic imagePath;

// Clients can use this method directly
// to forward-load a real image
// if there is no need to show this object
// on a view.
- (UIImage *) image
{
  if (realImage_ == nil)
  {
    realImage_ = [[UIImage alloc] initWithContentsOfFile:imagePath_];
  }

  return realImage_;
}

// A forward call will be established
// in a separate thread to get a real payload from
// a real image.
// Before a real payload is returned,
// drawRect: will handle the background
// loading process and draw a placeholder frame.
// Once the real payload is loaded,
// it will redraw itself with the real one.
- (void)drawRect:(CGRect)rect
{
  // if is no real image available
  // from realImageView_,
  // then just draw a blank frame
  // as a placeholder image
  if (realImage_ == nil)
  {
    // Drawing code
    CGContextRef context = UIGraphicsGetCurrentContext();

    // draw a placeholder frame
    // with a 10-user-space-unit-long painted
    // segment and a 3-user-space-unit-long
    // unpainted segment of a dash line
    CGContextSetLineWidth(context, 10.0);
    const CGFloat dashLengths[2] = {10,3};
    CGContextSetLineDash (context, 3, dashLengths, 2);
    CGContextSetStrokeColorWithColor(context, [[UIColor darkGrayColor] CGColor]);
    CGContextSetFillColorWithColor(context, [[UIColor lightGrayColor] CGColor]);
    CGContextAddRect(context, rect);
    CGContextDrawPath(context, kCGPathFillStroke);

    // launch a thread to load the real
    // payload if it hasn't done yet
    if (!loadingThreadHasLaunched_)
    {
[self performSelectorInBackground:@selector(forwardImageLoadingThread)
                             withObject:nil];
      loadingThreadHasLaunched_ = YES;
    }
  }
  // otherwise pass the draw*: message
  // along to realImage_ and let it
  // draw the real image
  else
  {
    [realImage_ drawInRect:rect];
  }
}

- (void) dealloc
{
  [realImage_ release];
  [super dealloc];
}

#pragma mark -
#pragma mark A private method for an image forward loading thread


- (void) forwardImageLoadingThread
{
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

  // forward loading the real
  // payload
  [self image];

  // redraw itself with the newly loaded image
  [self performSelectorInBackground:@selector(setNeedsDisplay) withObject:nil];

  [pool release];
}

@end

Wow! Congratulations, you made it! Since the implementation is huge, we can't cover every bit and piece of it. But we can summarize the whole proxy operation as follows:

  • The image property method will load a real image with [[UIImagealloc] initWithContentsOfFile:imagePath_] if realImage_ is not loaded. It eventually returns realImage_.

  • ScribbleThumbnailViewImageProxy is a subclass of UIView; its drawRect: method provides a custom drawing algorithm on the screen. In this case, it will draw either a placeholder image or an actual image if it is loaded. If the real image is not loaded, then spawn a new thread to forward the loading process defined in the image property method.

  • A thread method, forwardImageLoadingThread, is privately defined in an anonymous category. The method will execute the image property method so that a real image will be loaded as a UIImage object. Then a message of setNeedsDisplay to self will trigger a view content refresh. Since the real image is completely loaded at this point, the drawRect: method will draw the real UIImage object by forwarding a drawInRect: message to it instead of drawing the placeholder image.

There are a couple of things that are worth mentioning. The private member variable loadingThreadHasLaunched_ is used to determine if a thread for forwardImageLoadingThread is already launched in the drawRect: method. If so, it won't do anything except draw the same old placeholder image because the proxy requires loading the real image only once. If the proxy thumbnail view is located outside of the displayable area of the view, UITableView will destroy it. When the thumbnail view comes back into the view, then UITableView will reuse/construct it and the proxy forward-loading process will start all over again.

Using a Proxy Pattern in the Cocoa Touch Framework

Objective-C doesn't support multiple inheritances. So if your proxy objects are not subclasses of anything specific in the Cocoa Touch framework, you may consider using NSProxy for your placeholders or surrogate objects.

The NSProxy is a root class in the Cocoa framework just like NSObject. The NSProxy implements NSObject protocol, so an NSProxy object is also indeed an NSObject type. The NSProxy class is an abstract base class so it doesn't have its own initialization method. A call to any method that an NSProxy object doesn't know how to respond to will throw an exception.

The main purpose of NSProxy is to define an API for objects that act as stand-ins for other objects or for objects that don't exist yet. A message that is sent to a proxy object will be forwarded to the real object or cause the proxy to load or transform itself into the real object. Subclasses of the NSProxy can be used to implement lazy instantiation of objects that are expensive to create—for example, a large image from the file system, as in the example in the previous sections.

Even though NSProxy is regarded as a type of NSObject, its existence has only one purpose – being a proxy. There are two instance methods that are essential to make the whole proxy deal happen, forwardInvocation: and methodSignatureForSelector:. An NSProxy subclass doesn't even need to have other extra methods except, probably, an initialization method and some other useful properties. The key is that when an object of a NSProxy subclass doesn't respond to a method that would be available to a real object, then the Objective-C runtime will send a message of methodSignatureForSelector: to the proxy object for a correct method signature of the message being forwarded. The runtime will in turn use the returned method signature to construct an instance of NSInvocation and send it with a forwardInvocation: message to the proxy object so it will forward the invocation to other object. If the proxy object of NSProxy's subclass can respond to the message, then the forwardInvocation: method will not be invoked at all. Objective-C doesn't support multiple inheritances, but we can achieve that by using NSProxy's message forwarding mechanism to forward tasks that can be handled by other classes' objects.

A common example of the Proxy pattern in an iOS application is the Mail app. Any attachment received in a message will show only some basic information, such as the file name and its size, as shown in a screenshot in Figure 22-6.

An unloaded attachment in a message

Figure 22-6. An unloaded attachment in a message

When the user taps the placeholder image to start loading the content of the attachment, the icon will be replaced by a small progress view reporting the actual loading progress, as shown in Figure 22-7.

An attachment is being loaded in the Mail app.

Figure 22-7. An attachment is being loaded in the Mail app.

When the attachment is completely loaded, the actual image will be displayed in the message area, as shown in Figure 22-8.

A completely loaded image attached in a message

Figure 22-8. A completely loaded image attached in a message

The original attachment placeholder icon is a proxy for loading an actual image across the network when it's asked to do so. If the user never taps the placeholder icon, the attachment will never be loaded and only the basic information of the attachment will be shown. It saves not only network resources, but also memory and the waiting time when the Mail app is still being run.

Summary

Memory usage is always a concern for iOS application development. No matter what kind of iOS device your application is running on, lazy-loading techniques are always recommended for the sake of performance. You can apply the Proxy pattern to an iOS application to lazy-load any expensive data, such as large image files in the file system or large payload from a server through a slow network. A virtual proxy provides some lightweight information to clients that loading an expensive object is not required until it's requested to do so.

In the next part, we will see a different design pattern for abstracting a process of saving the state of an object.



[20] The original definition appeared in Design Patterns, by the "Gang of Four" (Addison-Wesley, 1994).

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

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