
Already many copies were broken about the topic “event handling in Objective-C”, about delegating events (for example,
viewWillAppear: (BOOL) animated ), about how it is not convenient when you need to listen to them simultaneously in different places of the program.
I want to offer you my implementation of the Observer template, which uses the power of C ++ 0x and allows you to declare signals with a hard-typed list of parameters, for example, like this:
new TLSignal<NSString *, BOOL>(self);
Since Since my knowledge of C ++ is pretty scanty, I would be grateful for any advice on how to improve this code.
')
Interested please under the cat.
The essence of the problem
There are often situations when it is necessary to notify other objects about any event (property changed, state change, etc.), and you must be able to comply with the “One-to-many” correspondence so that several listeners can simultaneously receive the event. For this reason, the option with delegates immediately disappears.
What options do Objective-C's built-in tools offer us?
- Notification Center - certainly, NC has its own field of application, but it is more suitable for global events within the entire program, just as the identification of events follows a string identifier, which is not good. Well, the performance of this solution leaves much to be desired, as well as the API.
- Key-Value Observing (KVO) is a special case of an event when some property of an object changes. It allows you to do wonderful things like Bindings, but the API reduces all its charms to nothing.
Observer pattern
Better than an
article on Wikipedia, I think it’s impossible to tell me, so I’ll just clarify the details:
- Listeners of our events will be the usual Objective-C blocks.
- The object that sends events using my signals is itself responsible for their creation and destruction.
- The types and number of parameters of the method that is executed during the notification will be set through C ++ templates and checked at the compilation stage.
So, the signal declaration looks like this:
new TLSignal<NSString *, BOOL>(self);
Which means that:
- We created a signal that takes 2 parameters: a string (NSString) and a boolean (BOOL)
- The target for our signal will be self, i.e. the object in which this signal is created.
In this case, the block listening to this event will look like this:
auto observerBlock = ^(id target, NSString *stringParam, BOOL boolParam) { NSLog(@"%@ %@ %d", target, stringParam, boolParam); };
Pay attention to the 1st parameter id target, it is always transmitted and is equal to the “holder” of this signal.
Usage example
A code without an example of use is not a code, so I want to give a small example of a program, by the example of which I would like to demonstrate what the actual advantage of signals is and how they can be used.
ExampleViewController.h
#import <UIKit/UIKit.h> #import "Signals.h" @interface ExampleViewController : UIViewController @property (nonatomic, readonly) TLSignal<UIView *> *viewDidLoadSignal; @end
Everything is simple here, we declare a signal with the viewDidLoadSignal identifier, which will transmit the UIView instance to the listeners.
ExampleViewController.mm
#import "ExampleViewController.h" @implementation ExampleViewController @synthesize viewDidLoadSignal = _viewDidLoadSignal; -(TLSignal<UIView *> *)viewDidLoadSignal { if(!_viewDidLoadSignal) { // ( ) _viewDidLoadSignal = new TLSignal<UIView *>(self); } return _viewDidLoadSignal; } -(void)viewDidLoad { [super viewDidLoad]; // , UIView self.viewDidLoadSignal->notify(self.view); } @end
AppDelegate.mm
#import "AppDelegate.h" #import "ExampleViewController.h" @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)options { UIWindow window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.viewController = [[ExampleViewController alloc] initWithNibName:@"ViewController" bundle:nil]; // , UIView self.viewController.viewDidLoadSignal->addObserver(^(TLSignal<UIView *> *signal, UIView *targetView) { targetView.backgroundColor = [UIColor blackColor]; }); self.window.rootViewController = self.viewController; [self.window makeKeyAndVisible]; return YES; } @end
Please note that the extension of files with code that uses signals must be .mm and not .m, otherwise it will be perceived as ordinary Objective-C code and will not understand our plus magic!Conclusion
I sincerely hope that something like this at the standard level will appear in Objective-C (for example, 2.5 or 3.0), but for now this has not happened, I suggest you use my library (it is extremely easy to use, the code is also understandable even with small knowledge in C ++, with which I have :)):
TLSignals on githubThe other day it will appear in Cocoapods, but until this happens, you can simply copy the
TLSignals.h file to your project and start working with it.
The code is partially covered with tests whose reading will help you better understand how it works:
TLSignalsTests.mm on GitHubI would be grateful for additions and improvements, as well as poking my nose at weak moments on the part of C ++ and in general, because we are all just learning;)
If you find typos or grammatical errors, please write about them with a personal message, so as not to clog these comments.
Thanks for attention!
Related Links