¶ ↑
KVOblocks for MacRubyInspired by KVO+Blocks for Objective-C, KVOblocks brings a cleaner API to using Key Value Observing in Macruby.
¶ ↑
Example UsageKey Value Observing can be very verbose and require a significant amount of boilerplate code. The KVOblocks module aims to remove all the excess code and create a succinct, readable API. Here is an example of what Key Value Observing looks like without using the KVOblocks module. We have a ‘Bus’ class that is initialized with a number of passengers. Let’s say these passengers are actually rhinos, they’re going to have quite an impact on fuel consumption, therefore every time the passenger number changes the miles_per_gallon needs to be recalculated:
class Bus attr_accessor :passengers def initialize(passengers) @passengers = passengers self.addObserver(self, forKeyPath:'passengers', options:0, context:nil) end def observeValueForKeyPath(path, ofObject:object, change:change, context:context) if object == self && path == 'passengers' calculate_miles_per_gallon else super end end end bus = Bus.new(2) bus.setPassengers(20) # calculate_miles_per_gallon, will be invoked
As can be seen from the above example, not only is the API more verbose than it needs to be, the code that is executed for the observed object needs to be placed in a different method call ‘observeValueForKeyPath(path, ofObject:object, change:change, context:context)’, which obscures the intent of the code. When several observers are added, this problem is compounded further as all observer code needs to be placed in this one method.
Here is the equivalent code using the KVOblocks module:
require 'KVOblocks' class Bus attr_accessor :passengers def initialize(passengers) @passengers = passengers self.add_observer_for_key_path('passengers') { calculate_miles_per_gallon } end end bus = Bus.new(2) bus.setPassengers(20) # calculate_miles_per_gallon, will be invoked
The observer and the code to be executed are defined together, greatly improving clarity. If calculate_miles_per_gallon is a CPU intensive operation we don’t want the main thread to become blocked, as the UI will become unresponsive. This problem can be solved with this tiny amendment to the code:
self.add_observer_for_key_path('passengers', async:true) { calculate_miles_per_gallon }
By including the ‘async:true’ argument, calculate_miles_per_gallon will occur in the background and the UI will remain responsive.