Your opinion on this alternative to notifications and delegates: Signals?

SO tells me that this question is subjective and will probably be closed. This is really subjective, because I ask the opinions of experienced Objective-C developers. Should I post this elsewhere? Please inform.


Quite unexpectedly for Objective-C, although it is pretty confident in the concept of writing OOP code, from the very beginning I struggled with the NSNotification vs Delegate dilemma. I wrote a few questions on this. I think I understand the point. Notifications are sent worldwide, so they cannot be used to notify nearby objects. Delegates exist to transfer tasks to another entity acting on behalf of the delegated entity. Although this can be used for closely related objects, I believe that the workflow is detailed (new class, new protocol, etc.), and only the word "delegation" makes me think about armies and bosses and generally makes me feel embarrassing.

Where I come from (AS3), there are things called events. They are halfway between delegates and NSNotifications, and largely manage the world of flash notifications, until recently, Mr. Robert Penner came and expressed his dissatisfaction with the events. So he wrote a library that is now widely used in the AS3 community called Signals. Inspired by C # events and signals / slots in Qt, these signals are actually properties of objects that you receive from the outside and add listeners. There you can do much more with the signal, but on it the core is what it is.

Since the concept is so modest, I gave it and wrote my own class of signals in Objective-C. I used Signal.h / .m here .

A way to use this to notify class A of an event in class B might look like this:

// In class b, assign a Signal instance to a retained property: self.awesomeThingHappened = [[[Signal alloc] init] autorelease]; // In class a, owner of class b, listen to the signal: [b.awesomeThingHappened add:self withSelector:@selector(reactToAwesomeThing)]; // And when something actually happens, you dispatch the signal in class b: [self.awesomeThingHappened dispatch]; // You might even pass along a userInfo dictionary, your selector should match: [self.awesomeThingHappened dispatchWithUserInfo:userInfo]; 

I hope that he adheres to the correct memory management rules, but when the signal is canceled, he should automatically remove all listeners and skip silently. Such a signal should not be a general substitute for notification and delegation, but there are many close situations where I feel that the signal is cleaner than the other two.

My question for stackoverflow is what do you think of such a solution? Can you remove this from your project immediately if one of your interns puts it in? Would you fire your employee if he has already completed an internship? Or maybe there is already something similar, but much more grandiose than you use instead?

Thanks for your time, EP.


EDIT: Let me give you a concrete example of how I used this in an iOS project.

Consider this scenario of four types of property with nested ownership. There is a view manager that owns a window manager that owns several windows, each of which has a view with controls, including a close button. There is probably a design flaw here, but this is not an example point: P

Now that the close button is pressed, the gesture recognizer starts the first selector in the window object. It is necessary to notify the window manager that it is closing. Then the window manager can decide whether another window will appear, or if all windows remain hidden all together, and at this moment the view manager needs to get a relief to enable scrolling on the main view.

The notifications from the window to the window manager and the window manager for viewing the controller are those that I have now implemented using signals. Perhaps this was a case of delegation, but for just a “closed” action, it seems pretty verbose to create two delegate protocols. On the other hand, since the relationship of these objects is very well defined, this is also not like the case of NSNotifications. There also has not changed the value that I could observe with KVO, because it is just a click of a button. Listening to some kind of "hidden" state might make me have this flag reset when the window is reopened, which makes it difficult to understand and a little error prone.

+4
source share
4 answers

Well, having massaged the answers and comments a bit, I think I came to the conclusion that the Signal class, borrowed from AS3, has very little reason to exist in Objective-C / Cocoa. There are several patterns in Cocoa that cover the ranges of use that I thought of coverage using the Signal class. This may seem very trivial for more experienced Cocoa developers, but it was difficult for me to get the full spectrum.

I tried to formulate this quite briefly, but please correct me if I am wrong.

Target action

Used only to notify your application of user interaction (mainly affects). From what I saw and read, there is no way to “borrow” the target action system for your own use.

KVO (monitoring key value)

Very useful for receiving notifications when values ​​change in accessible objects. Not so useful for notification of specific events that have no meaning associated with them, such as timer events or subsequent events.

NSNotification

Very useful for receiving notifications when changing values ​​or other events in less accessible objects. Due to the broadcast nature of the notification center, this is less suitable for cases where objects have a direct link to another.

Delegation

Uses most lines of code compared to the other three, but is also most suitable when the other three are not. Use this when one object needs to be notified of specific events in another. Delegates should not be abused for accessing the methods of the owner object. Stick to methods like "must," "will," and "done."

Signal

It was a fun experiment, but I mostly used it for situations with a classic delegation. I also used it to bypass related delegates (c delegate from b, b delegate a, where a fires an event that should do this c), without resorting to NSNotification.

I still think there should be a more elegant solution for this edge case, but for now, I just stick to the existing framework. If anyone has a fix or other notification concept, please let me know. Thank you for your help!

+2
source

This is an interesting idea, but I think I do not see what is very different from Cocoa's notification center. Compare and compare:

 self.awesomeThingHappened = [[[Signal alloc] init] autorelease]; // Your signals library // Cocoa notifications (no equivalent code) [b.awesomeThingHappened add:self withSelector:@selector(reactToAwesomeThing)]; // Your signals library [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reactToAwesomeThing:) name:@"AwesomeThingHappened" object:n]; // Cocoa notifications [self.awesomeThingHappened dispatch]; // Your signals library [[NSNotificationCenter defaultCenter] postNotificationName:@"AwesomeThingHappened" object:self]; // Cocoa notifications [self.awesomeThingHappened dispatchWithUserInfo:userInfo]; // Your signals library [[NSNotificationCenter defaultCenter] postNotificationName:@"AwesomeThingHappened" object:self userInfo:userInfo]; // Cocoa notifications 

So, okay. I don’t think you are trying to say that the line-for-line Signals library for Cocoa is different; rather, the argument is that it is a clutch term, it is not as tight as delegates, but not as loose as notifications. To this end, I think, I wonder how it is necessary? I guess I see that I can say that "this object" A "is heavily dependent on" B ", but does not have to be combined with all this," but to be honest, this seems like a somewhat rare situation.

Anyway, NSNotificationCenter and its ilk, as well as delegates, are pretty standard in Cocoa applications. I always use the rule of thumb: if you deviate from the standard, even the standard de facto, you must have a good reason. If you have a good reason not to use either NSNotificationCenter or delegates, you might have a good reason to use this Signals setting. (And as an aside, I hesitate to associate notifications and delegates - each of them has its own role and exists for various reasons.)

It is hard to say more without a specific use case. I tend to say, "Hey, that looks great, but it looks like it is filling the role already filed with alerts." Do you have specific use cases that you could name?

+1
source

What do you think of this decision?

I do not see what good. For me, it looks like a combination of target / actions + notifications (you can have several target / actions for one notification event, but the t / a pair is registered by the object itself, and not by the global notification center). In fact, it is more like observing key values ​​in this way, except that KVO is limited by observable properties.

Do you immediately remove this from your project if one of your interns puts it in?

No. This is not bad code. In fact, it looks somehow neat. But I just do not see any obvious benefit in this.

Would you fire your employee if he has already completed an internship?

Of course not. You do not force people to write good code.

Perhaps there is already something similar, but much more grandiose than you used?

If you really wanted to do it neatly, change the API to use blocks instead. Then you can do:

 [object dispatchOnAwesomeThingHappened:^{ NSLog(@"holy cow, something awesome just happened!"); }]; 

Again, however, you will be limited to reacting to things that objects are clearly “sending”. It would be much more accurate if you could attach the material immediately before and / or after any arbitrary method call. If you are interested, I checked Aspect Objective-C on github .

0
source

I think there is a gap between NSNotifications and delegates. And KVO has the worst API in all Cocoa .

Regarding NSNotificationCenter , here are some of its problems:

  • it is subject to typos
  • it’s difficult to track the observers of this object, so it’s difficult to debug
  • It is very verbose
  • you can only transmit data in a dictionary, which means that you cannot use weak links or structures unless you wrap them. (see: very verbose)
  • doesn't play well with GCD : limited queue support (blocks only)

So there is a definite need for something better.

I created my own observable class , which I use for each project. Another alternative is to use ReactiveCocoa RACSignal .

0
source

Source: https://habr.com/ru/post/1336847/


All Articles