Chapter 18: Flyweight Pattern
大綱
When should you use it?
The flyweight pattern is a structural design pattern that minimizes memory usage and processing
The flyweight pattern has objects, called flyweights, and a static method to return them.
The flyweight pattern is a variation on the singleton pattern.
In the flyweight pattern, you usually have multiple different objects of the same class.

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.
Playground example
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) // true, 表示red和red2是共享同一個memory address
// 但不是UIColor中每個方法都是遵守Flyweights pattern
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) // false, custom color並不是Flyweights pattern
如何讓custom color也遵守Flyweights pattern?
extension UIColor {
// 1: 建立一個static dic來存RGBA
public static var colorStore: [String: UIColor] = [:]
// 2: 建立一個function
public class func rgba(_ red: CGFloat,
_ green: CGFloat,
_ blue: CGFloat,
_ alpha: CGFloat) -> UIColor {
// 利用RGBA組合當key
let key = "\(red)\(green)\(blue)\(alpha)"
if let color = colorStore[key] {
// 找到對應的key就回傳對應color
return color
}
// 3: 沒有在目前colorStore中,就存入
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) // proves this is a flyweight class method
What should you be careful about?
be careful about how big your flyweight memory grows.
you minimize memory usage for the same color, but you can still use too much memory in the flyweight store.
為什麼apple的UIColor只針對某些特定color是遵守Flyweights pattern, 像藍色,紅色。至於客製化顏色都弄成Flyweights pattern就會導致colorStore過於龐大。
Set bounds on how much memory you use or register for memory warnings and respond by removing some flyweights from memory
LRU (Least Recently Used) cache to handle this.
flyweight shared instance must be a class and not a struct. Structs use copy semantics
Tutorial project
希望每次切換tab可以切換指定的不同大小的字型。
每次切換都不需要再次產生instace因此可以利用Flyweight Pattern來處理。

Fonts Class
public final class Fonts {
// 1. 3個flyweight object分別表示不同字型
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)
private static let fontName = "coolstory-regular"
// 2. 利用static function來回傳對應的flyweight object
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)
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)
return UIFont(name: fontName, size: size)!
}
}
// MARK: - 切換tab
// 每次切換到對應的字型時,都只會產生一次object, 不會重複產生
@IBAction public func segmentedControlValueChanged(_ sender: UISegmentedControl) {
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
The flyweight pattern minimizes memory usage and processing.
This pattern has objects, called flyweights, and a static method to return them. It’s a variation on the singleton pattern.
When creating flyweights, be careful about the size of your flyweight memory. If you’re storing several flyweights, it’s still possible to use too much memory in the flyweight store.
Examples of flyweights include caching objects such as images, or keeping a pool of objects stored in memory for quick access.
Last updated
Was this helpful?