Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
manual:enhanced_events [2013/05/28 15:52]
daniel [Block-based Event Listeners]
manual:enhanced_events [2014/02/05 09:54] (current)
daniel [Conclusion]
Line 1: Line 1:
 +====== Enhanced Events ======
 +
 +===== Simplified Event Dispatching =====
 +
 +To dispatch an event, you create an event object and pass it to the "​dispatchEvent:"​ method --- you've seen that already.
 +
 +<code objc>
 +SPEvent *event = [SPEvent eventWithType:​EVENT_TYPE];​
 +[self dispatchEvent:​event];​
 +</​code>​
 +
 +What I didn't show you before is that there is an alternative method that does just the same, but is more concise: you can let Sparrow create the event for you and dispatch it right away. 
 +
 +<code objc>
 +[self dispatchEventWithType:​EVENT_TYPE];​
 +
 +// or
 +[self dispatchEventWithType:​EVENT_TYPE bubbles:​YES];​
 +</​code>​
 +
 +That has exactly the same effect as the previous sample. Note, however, that this only works with standard events; if you need to dispatch a custom event subclass, you have to use the traditional method.
 +
 +===== Block-based Event Listeners =====
 +
 +A rather new feature of Objective-C are "​blocks"​. Think about a block as an inline-function that you can define anywhere in your code. In many situations, blocks allow you to simplify your code.
 +
 +That's why Sparrow 2.0 introduced block-based event listeners. In the previous section, you saw event handlers declared at class level: a method would be called on a certain object when an event was dispatched.
 +
 +The concise alternative to this approach is to define the event listener as a block, like this:
 +
 +<code objc>
 +[button addEventListenerWithType:​SP_EVENT_TYPE_TRIGGERED block:​^(SPEvent *)event
 +{
 +   ​NSLog(@"​button triggered"​);​
 +}];
 +</​code>​
 +
 +The thing starting with the caret (^) is the block. In essence, it's really nothing else than a function with one parameter of type "​SPEvent *".
 +
 +Block event handlers have several advantages:
 +
 +  * You can add your event handler directly inline,
 +  * which is less code to type and
 +  * allows you to directly access any variables of the outer scope.
 +
 +Let's look at a more complete example. We want to create a button that toggles the visibility of an image object.
 +
 +<code objc>
 +- (id)init
 +{
 +    if ((self = [super init]))
 +    {
 +        SPImage *image = [SPImage imageWithTexture:​...];​
 +        SPButton *button = [SPButton buttonWithUpState:​...];​
 +        ​
 +        [button addEventListenerWithType:​SP_EVENT_TYPE_TRIGGERED block:​^(SPEvent *)event
 +        {
 +            image.visible = !image.visible;​
 +        }];
 +    }
 +}
 +</​code>​
 +
 +Note that we didn't have to create a new instance method; the block is defined directly inline. But what's even more interesting is that we could access the "​image"​ object directly in the block, even though it's not an instance variable of the class, but just a local variable. ​
 +
 +If we used the conventional,​ selector-based event handler, we would have to convert the button object to an instance variable in order to be able to access it from the method.
 +
 +==== Beware of Retain Cycles! ====
 +
 +If you use this type of event handler, there is one thing you have to be careful about, though. Per default, a block will "​retain"​ any object that is used inside it. That can easily lead to circular references, in other words: memory leaks.
 +
 +Let's look at an example.
 +
 +<code objc>
 +@implementation Game
 +
 +- (id)init
 +{
 +    if ((self = [super init]))
 +    {
 +        [self addEventListenerForType:​SP_EVENT_TYPE_ADDED_TO_STAGE block:^(id event)
 +        {
 +            [self startAnimations];​
 +        }];
 +    }
 +}
 +
 +- (void)startAnimations
 +{
 +    // ...
 +}
 +
 +@end
 +</​code>​
 +
 +When you compile this, Xcode will display the following warning:
 +
 +  Capturing '​self'​ strongly in this block is likely to lead to a retain cycle.
 +
 +<note warning>
 +Do not ignore this warning! If you do, you will certainly leak some memory.
 +</​note>​
 +
 +The reason for this is that the block retains "​self"​ (because it's used in the block), and "​self"​ retains the block (because it stores it in an internal list of event listeners). Such a situation is called "​retain cycle" and will prevent the object from ever being released again.
 +
 +Thankfully, that situation is easy to solve. You can create a new variable that references "​self"​ only weakly, and use this variable instead of the other one inside the block.
 +
 +<code objc>
 +__weak Game *weakSelf = self;
 +
 +[self addEventListenerForType:​SP_EVENT_TYPE_ADDED_TO_STAGE block:^(id event)
 +{
 +    [weakSelf startAnimations];​
 +}];
 +</​code>​
 +
 +This will not only happen when you access "​self",​ but also for any member variable of the class. The solution is just the same: create a "​weak"​ pointer to that variable and use that in the block instead of directly accessing the member variable.
 +
 +==== Changing Variables in the Block ====
 +
 +In the following sample, we want to count the number of times someone has pressed a button. This is our first attempt at doing so:
 +
 +<code objc>
 +int hitCount;
 +        ​
 +[button addEventListenerForType:​SP_EVENT_TYPE_TRIGGERED block:^(id event)
 +{
 +   ​hitCount++;​
 +   ​NSLog(@"​Number of hits: %d", hitCount);
 +}];
 +</​code>​
 +
 +Unfortunately,​ Xcode is not happy: it displays the following error message:
 +
 +  Variable is not assignable (missing __block type specifier)
 +
 +This time, Xcode was even nice enough to tell us about the solution! Just add a block qualifier to the variable.
 +
 +<code objc>
 +__block int hitCount;
 +</​code>​
 +
 +That will fix the error.
 +
 +==== Conclusion ====
 +
 +As you can see, blocks are not 100% fail-safe. Still: if you listen to Xcode'​s warnings and errors, you should be on the safe side. In exchange, you will be able to massively simplify some of your code with the help of blocks.
 +
 +
 +
 +-------
 +
 +//Next Section: [[Touch Events]]
  
  manual/enhanced_events.txt · Last modified: 2014/02/05 09:54 by daniel
 
Powered by DokuWiki