[MacRuby-devel] kvo vs referenced hash

Brian Chapados chapbr at gmail.com
Tue Sep 8 20:59:26 PDT 2009


Yes, it could work that way too.  In this scenario where you just need
to call #update, if the game controller keeps an array/set of child
items, you could just call

[gameItems makeObjectsPerformSelector:@selector(update)];

You would use KVO in the following hypothetical scenario, which is
adopted from one of the chapters in Hillegass. It's a great pattern
showing how to use KVO to support change management + undo support.

Imagine your game items are "bugs" crawling around the screen.  The
player can click/tap on them, which makes them change color. They can
also change color in other game scenarios, which may not require
direct interaction by the player.  You'd have a "Bug" model class,
with a color property.

Assuming you have an array-like property named 'bugs'...
---
note:
You could get that by actually having an NSArray *bugs, or by
implementing the methods below, which could manipulate some other
state...

- (NSArray *) bugs;
- (NSUInteger) countOfBugs;
- (Bug *) objectInBugsAtIndex:(NSUInteger)index;
----

When the game controller adds a bug, your insert method will get called...

- (void) insertObject:(Bug *)aBug inBugsAtIndex:(NSUInteger)index
{
  // start observing the color property...
  [self startObservingBug:aBug];
}

- (void) startObservingBug:(Bug *)aBug
{
  [aBug addObserver:self forKeyPath:@"color"
options:NSKeyValueObservingOptionOld context:nil];
}

In the game controller, you can respond respond to bugs changing color
for any reason by implementing:

- observeValueForKeyPath:ofObject:change:context:
in this method:
- record an "undo" action so that the user can take back the action
that caused the change, using a setter than can handle inverse
actions, something like:

- (void) changeKeyPath:(NSString *)keyPath ofObject:(id)object toValue:(id)value
{
  [object setValue:value forKeyPath:keyPath];
}

when the bug dies or gets removed from the array, you handle it here:

- (void) removeObjectFromBugsAtIndex:(NSUInteger)index
{
  // stop observing:
  [self stopObservingBug:[bugs objectAtIndex:index]];
}

- (void) stopObservingBug:(Bug *)aBug
{
  [aBug removeObserver:self forKeyPath:@"color"];
}

This example uses both KVC & KVO.  Powerful stuff.

Brian

On Tue, Sep 8, 2009 at 1:54 PM, Matt Aimonetti<mattaimonetti at gmail.com> wrote:
> The project is a simple demo game using the same approach explained by Brian
> where game items listen for notifications and update themselves based on the
> notification.
> For some reasons, I think it would be easier for the game controller to know
> about the game items and call #update on each of them directly.
>
> - Matt
>
>
>
> On Tue, Sep 8, 2009 at 1:18 PM, Brian Chapados <chapbr at gmail.com> wrote:
>>
>> I am also not sure that I fully understand this scenario, but it
>> doesn't appear that you would need KVO. Do the "tasks" or the "Brain"
>> have any properties to observe?
>>
>> As Ben points out, KVO really shines when you need to keep a UI in
>> sync with the state of a model.
>>
>> If "tasks" just need to know when the "Brain" is done thinking, I
>> would just use standard notifications.
>>
>> NSString * const BrainFinishedThinkingNotification =
>> @"BrainFinishedThinkingNotification"
>>
>> When the Brain has a thought... send a notification:
>>
>> (inside Brain class)
>>
>> - (void) thinkOfSomething
>> {
>>   // create idea
>>
>>  // send notification when finished
>>  [[NSNotificationCenter defaultCenter]
>> postNotificationName:BrainFinishedThinkingNotification
>> withObject:self];
>>  // (you could include extra data in a userInfo object)
>> }
>>
>> That's it, the Brain doesn't have to know about Tasks.
>>
>> The task class can then register for changes, and respond to the
>> notification:
>>
>> [[NSNotification defaultCenter] addObserver:self
>> selector:@selector(brainFinishedThinking:)
>> name:BrainFinishedThinkingNotification object:nil];
>>
>> handle them in:
>> - (void) brainFinishedThinking:(NSNotification *)n
>> {
>>  // access the Brain if necessary
>>  Brain *theBrain = (Brain *)[n object];
>>
>>  // do something
>> }
>>
>> Tasks don't need an explicit reference to Brain.
>>
>> Brian
>>
>> On Sat, Sep 5, 2009 at 2:16 AM, Matt Aimonetti<mattaimonetti at gmail.com>
>> wrote:
>> > Hi list,
>> >
>> >  Coming from a Ruby background, I try to understand and pick the best
>> > from
>> > Obj-C/Cococa and learn how to use brilliant ideas to improve my coding.
>> >
>> > Tonight I was working on a simple demo app to learn how things are done
>> > in
>> > the Obj-C world and see how they would transpose to the MacRuby world.
>> >
>> > Everything went well until the KVO question came along.
>> >
>> > Let's say I have a controller that we are going to call Brain.
>> > We also have a lot of simpler objects that don't do anything but having
>> > a
>> > state and being displayed, we are going to call them Task instances. The
>> > brain is called every X seconds to "think".
>> >
>> > My understanding is that an Obj-C developer would register all the tasks
>> > instance with the brain using a KVO and the brain would send
>> > notifications
>> > when it's being called to "think".  So, every time a new task is being
>> > created, an observer is added on the brain with the new task key. Each
>> > Task
>> > instance has an observeValueForKeyPath:ofObject:change:context: method
>> > implemented which checks that the notification is meant for itself and
>> > if it
>> > is, to act accordingly.
>> >
>> > While the concept is simple and attractive. I wonder if it wouldn't be
>> > simpler to forget about kvo's and simply keep the list of registered
>> > tasks
>> > in the brain and the brain would call the tasks directly and tell them
>> > to
>> > change.
>> >
>> > It sounds to me that in the MacRuby world, it would be more efficient.
>> > If we
>> > have 250 tasks for instance, when a notification is being sent, every
>> > single
>> > task in the 250 tasks created, will receive the notification and will
>> > check
>> > what to do with it. If we had a simple hash/dictionary in the brain, we
>> > could directly find the task to handle and call it directly without
>> > having
>> > to go through the 250.
>> >
>> > Am I missing something or in this specific case and because we use Ruby,
>> > KVO
>> > aren't the way to go?
>> >
>> > Thanks for your help,
>> >
>> > - Matt
>> >
>> > _______________________________________________
>> > MacRuby-devel mailing list
>> > MacRuby-devel at lists.macosforge.org
>> > http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
>> >
>> >
>> _______________________________________________
>> MacRuby-devel mailing list
>> MacRuby-devel at lists.macosforge.org
>> http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
>
>
> _______________________________________________
> MacRuby-devel mailing list
> MacRuby-devel at lists.macosforge.org
> http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
>
>


More information about the MacRuby-devel mailing list