Home iOS & Swift Books Design Patterns by Tutorials

18
Flyweight Pattern Written by Jay Strawn

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

The flyweight pattern is a structural design pattern that minimizes memory usage and processing.

This pattern provides objects that all share the same underlying data, thus saving memory. They are usually immutable to make sharing the same underlying data trivial.

The flyweight pattern has objects, called flyweights, and a static method to return them.

Does this sound familiar? It should! The flyweight pattern is a variation on the singleton pattern. In the flyweight pattern, you usually have multiple different objects of the same class. An example is the use of colors, as you will experience shortly. You need a red color, a green color and so on. Each of these colors are a single instance that share the same underlying data.

When should you use it?

Use a flyweight in places where you would use a singleton, but you need multiple shared instances with different configurations. If you have an object that’s resource intensive to create and you can’t minimize the cost of creation process, the best thing to do is create the object just once and pass it around instead.

Playground example

Open AdvancedDesignPatterns.xcworkspace in the starter directory and then click on the Flyweight link to open the page. Here, you’ll use UIKit. Flyweights are very common in UIKit. UIColor, UIFont, and UITableViewCell are all examples of classes with flyweights.

import UIKit

let red = UIColor.red
let red2 = UIColor.red
print(red === red2)
let color = UIColor(red: 1, green: 0, blue: 0, alpha: 1)
let color2 = UIColor(red: 1, green: 0, blue: 0, alpha: 1)
print(color === color2)
extension UIColor {
  
  // 1
  public static var colorStore: [String: UIColor] = [:]
  
  // 2
  public class func rgba(_ red: CGFloat,
                         _ green: CGFloat,
                         _ blue: CGFloat,
                         _ alpha: CGFloat) -> UIColor {
    
    let key = "\(red)\(green)\(blue)\(alpha)"
    if let color = colorStore[key] {
      return color
    }
    
    // 3
    let color = UIColor(red: red,
                        green: green,
                        blue: blue,
                        alpha: alpha)
    colorStore[key] = color
    return color
  }
}
let flyColor = UIColor.rgba(1, 0, 0, 1)
let flyColor2 = UIColor.rgba(1, 0, 0, 1)
print(flyColor === flyColor2) 

What should you be careful about?

In creating flyweights, be careful about how big your flyweight memory grows. If you’re storing several flyweights, as in colorStore above, you minimize memory usage for the same color, but you can still use too much memory in the flyweight store.

Tutorial project

Throughout this section, you’ll create a tutorial app called YetiJokes.

import Foundation

public final class Fonts {

  // 1
  public static let large = loadFont(name: fontName,
                                     size: 30.0)
  public static let medium = loadFont(name: fontName,
                                      size: 25.0)
  public static let small = loadFont(name: fontName,
                                     size: 18.0)
  
  // 2
  private static let fontName = "coolstory-regular"
  
  // 3
  private static func loadFont(name: String,
                               size: CGFloat) -> UIFont {

    if let font = UIFont(name: name, size: size) {
      return font
    }

    let bundle = Bundle(for: Fonts.self)
    
    // 4
    guard 
      let url = bundle.url(forResource: name,
                           withExtension: "ttf"),
      let fontData = NSData(contentsOf: url),
      let provider = CGDataProvider(data: fontData),
      let cgFont = CGFont(provider),
      let fontName = cgFont.postScriptName as String? else {
        preconditionFailure("Unable to load font named \(name)")
    }
    
    CTFontManagerRegisterGraphicsFont(cgFont, nil)
    
    // 5
    return UIFont(name: fontName, size: size)!
  }
}

import YetiTheme
// MARK: - View Life Cycle
public override func viewDidLoad() {
  super.viewDidLoad()

  textLabel.font = Fonts.small
}
switch sender.selectedSegmentIndex {
case 0:
  textLabel.font = Fonts.small
case 1:
  textLabel.font = Fonts.medium
case 2:
  textLabel.font = Fonts.large
default:
  textLabel.font = Fonts.small
}

Key points

You learned about the flyweight pattern in this chapter. Here are its key points:

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.

Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:

© 2021 Razeware LLC

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.