您对通知和委托的替代方案的看法:信号?

发布于 2024-10-14 15:29:12 字数 2047 浏览 3 评论 0原文

SO 告诉我这个问题是主观的,可能会被关闭。这确实是主观的,因为我正在征求经验丰富的 Objective-C 开发人员的意见。我应该将其发布到其他地方吗?请指教。


对 Objective-C 相当陌生,尽管对编写 OOP 代码的概念相当有信心,但我从一开始就一直在 NSNotification 与 Delegate 的困境中挣扎。我单独发布了一些关于该主题的问题。我想我确实明白了要点。通知是全局广播的,因此不应用于通知密切相关的对象。委托的存在是将任务移交给代表委托对象执行操作的其他对象。虽然这可以用于密切相关的对象,但我发现工作流程很冗长(新类、新协议等),并且“委托”一词本身就让我想到了军队和老板,总的来说让我感到不安。

我来自的地方(AS3)有一种叫做“事件”的东西。他们介于 delegates 和 NSNotifications 之间,几乎统治了 flash notification 的世界,直到最近,Robert Penner 先生出现并表达了他对事件的不满。因此,他编写了一个名为 Signals 的库,现已在 AS3 社区中广泛使用。受到 C# 事件和 Qt 中的信号/槽的启发,这些信号实际上是对象的属性,您可以从外部访问它们并向其添加侦听器。您还可以利用信号做更多事情,但其核心仅此而已。

因为这个概念太简单了,所以我尝试了一下,用 Objective-C 编写了自己的信号类。 我在这里列出了 Signal.h/.m

使用它来通知 A 类 B 类中的事件的方法可能如下所示:

// 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];

我希望它遵守正确的内存管理规则,但是当信号释放时,它应该自动删除所有侦听器并默默地消失。像这样的信号不应该是通知和委托的通用替代品,但在很多近距离柜台情况下,我觉得信号比其他两个更干净。

我向 stackoverflow 提出的问题是,您对这样的解决方案有何看法?如果您的一名实习生将其放入您的项目中,您会立即将其从您的项目中删除吗?如果你的员工已经完成实习,你会解雇他吗?或者是否已经有类似但更宏伟的东西可供您使用?

感谢您抽出时间,EP。


编辑:让我举一个具体的例子来说明我如何在 iOS 项目中使用它。

考虑这种具有嵌套所有权的四种对象类型的场景。有一个视图控制器拥有一个窗口管理器,拥有多个窗口,每个窗口拥有一个带控件的视图,其中有一个关闭按钮。这里可能存在设计缺陷,但这不是示例的重点:P

现在,当点击关闭按钮时,手势识别器会触发窗口对象中的第一个选择器。这需要通知窗口管理器它正在关闭。然后,窗口管理器可以决定是否出现另一个窗口,或者窗口是否完全隐藏,此时视图控制器需要获得碰撞以启用主视图上的滚动。

从窗口到窗口管理器以及从窗口管理器到视图控制器的通知是我现在用信号实现的。这可能是委托的情况,但对于“关闭”操作来说,创建两个委托协议似乎非常冗长。另一方面,因为这些对象的耦合定义得很好,所以它看起来也不像是 NSNotifications 的情况。我也无法通过 KVO 观察到真正的值变化,因为它只是一个按钮点击。监听某种“隐藏”状态只会让我在重新打开窗口时必须重置该标志,这使得它更难以理解并且容易出错。

SO is telling me this question is subjective and likely to be closed. It is indeed subjective, because I'm asking for the opinion of experienced Objective-C developers. Should I post this somewhere else? Please advise.


Fairly new to Objective-C, though fairly confident in the concept of writing OOP code, I've been struggling with the NSNotification vs Delegate dilemma from the start. I've posted a few questions about that subject alone. I do get the gist, I think. Notifications are broadcasted globally, so shouldn't be used for notifying closely related objects. Delegates exist to hand over tasks to other object, that act on behalf of the delegated object. While this can be used for closely related objects, I find the workflow to be verbose (new class, new protocol, etc), and the word "delegation" alone makes me think of armies and bosses and in general makes me feel uneasy.

Where I come from (AS3) there are things called Events. They're halfway between delegates and NSNotifications and pretty much ruled the world of flash notifying, until fairly recently, a Mr. Robert Penner came along and expressed his dissatisfaction with events. He therefore 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 access from the outside and add listeners to. There's much more you can do with a signal, but at it's core, that's it.

Because the concept is so humble, I gave it a go and wrote my own signal class in Objective-C. I've gisted Signal.h/.m here.

A way to use this for notifying class A of an event in class B could 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 it adheres to the right memory management rules, but when the signal deallocs, it should automatically remove all listeners and pass away silently. A signal like this isn't supposed to be a generic replacement of notification and delegation, but there are lot's of close counter situations where I feel a Signal is cleaner than the other two.

My question for stackoverflow is what do you think of a solution like this? Would you instantly erase this from your project if one of your interns puts it in? Would you fire your employee if he already finished his internship? Or is there maybe already something similar yet much grander out there that you'd use instead?

Thanks for your time, EP.


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

Consider this scenario of four object types with nested ownership. There's a view controller owning a window manager, owning several windows, each owning a view with controls, among which a close button. There's probably a design flaw in here, but that's not the point of the example :P

Now when the close button is tapped, a gesture recognizer fires the first selector in the window object. This needs to notify the window manager that it's closing. The window manager may then decide whether another window appears, or whether the windows stay hidden alltogether, at which point the view controller needs to get a bump to enable scrolling on the main view.

The notifications from window to window manager, and from window manager to view controller are the ones I've now implemented with Signals. This might have been a case of delegation, but for just a 'close' action, it seemed so verbose to create two delegate protocols. On the other hand, because the coupling of these objects is very well defined, it also didn't seem like a case for NSNotifications. There's also not really a value change that I could observe with KVO, because it's just a button tap. Listening to some kind of 'hidden' state would only make me have to reset that flag when reopening a window, which makes it harder to understand and a little error prone.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

缘字诀 2024-10-21 15:29:13

好吧,在对答案和评论进行了一番腌制之后,我想我已经得出了这样的结论:我从 AS3 借用的 Signal 类在 Objective-C/Cocoa 中几乎没有存在的理由。 Cocoa 中有几种模式涵盖了我想要用 Signal 类涵盖的使用范围。对于更有经验的 Cocoa 开发人员来说,这似乎非常微不足道,但对我来说,很难获得完整的频谱。

我试着把它写得相当简洁,但如果我有错,请纠正我。

Target-Action

仅用于通知您的应用程序用户交互(主要是触摸)。根据我所看到和阅读的内容,没有办法“借用”目标操作系统供您自己使用。

KVO(键值观察)

对于在可访问对象中的值发生更改时接收通知非常有用。对于通知没有附加值的特定事件(例如计时器事件或接口后续事件)来说不太有用。

NSNotification

对于在难以访问的对象中发生值更改或其他事件时接收通知非常有用。由于通知中心的广播性质,这不太适合对象直接引用另一个对象的情况。

与其他三个相比,委派

占用的代码行数最多,但在其他三个不适合的情况下也是最合适的。当一个对象应被通知另一对象中的特定事件时,请使用此对象。不应滥用委托来仅访问所有者对象的方法。坚持使用“应该”、“将”和“做了”等方法。

Signal

这是一个有趣的实验,但我主要将其用于经典的委托情况。我还使用它来规避链接委托(b 的 c 委托,a 的 b 委托,其中 a 启动应该使其到达 c 的事件),而不想诉诸 NSNotification。

我仍然认为对于这种边缘情况应该有一个更优雅的解决方案,但现在我会
只需坚持现有框架即可。如果有人有更正或其他通知概念,请告诉我。感谢您的帮助!

Alright, after marinating the answers and comments for a bit, I think I have come to a conclusion that the Signal class I borrowed from AS3, has very little reason for existence in Objective-C/Cocoa. There are several patterns in Cocoa that cover the ranges of use that I was thinking of covering with the Signal class. This might seem very trivial to more experienced Cocoa developers, but it for me it was hard to get the spectrum complete.

I've tried to put it down fairly concisely, but please correct me if I have them wrong.

Target-Action

Used only for notifying your application of user interaction (touches, mostly). From what I've seen and read, there's no way to 'borrow' the target-action system for your own use

KVO (key value observing)

Very useful for receiving notifications when values change in accessible objects. Not so useful for notifying specific events that have no value attached to them, like timer events or interface followup events.

NSNotification

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

Delegation

Takes the most lines of code compared to the other three, but is also most suitable when the other three are not. Use this one when one object should be notified of specific events in the other. Delegates should not be abused for just accessing methods of the owner object. Stick to methods like 'should', 'will' and 'did'.

Signal

It was a fun experiment, but I mostly used this for classic delegation situations. I also used it to circumvent linked delegates (c delegate of b, b delegate of a, where a starts the event that should make it to c) without wanting to resort to NSNotification.

I still think there should be a more elegant solution for this edge case, but for now I'll
just stick to the existing frameworks. If anyone has a correction or another notification concept, please let me know. Thanks for your help!

鲜血染红嫁衣 2024-10-21 15:29:13

这是一个有趣的想法,但我想我看不出它与 Cocoa 的通知中心有何显着不同。比较和对比:

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

所以,好吧。我认为您并不是想说,逐行的 Cocoa 信号库是不同的;相反,争论认为它是耦合的,它不像委托那么紧密,但也不像通知那么松散。为此,我想我想知道它有多大必要?我想我可以看到有点需要说“这个对象‘A’严重依赖‘B’,但不需要耦合得那么紧密”,但说实话,这似乎是一种罕见的情况。

无论如何,NSNotificationCenter 及其同类以及委托在 Cocoa 应用程序中都是相当标准的。我总是使用经验法则,如果你偏离了标准,即使是事实上的标准,你也应该有充分的理由。如果您有充分的理由既不使用 NSNotificationCenter 也不使用委托,那么您可能有充分的理由使用此信号设置。 (顺便说一句,我对将通知和委托关联起来犹豫不决——它们每个人都有一个角色,并且因不同的原因而存在。)

如果没有特定的用例,很难说更多。我倾向于说,“嘿,它看起来很酷,但看起来它填补了通知已经扮演的角色。”您有什么可以引用的具体用例吗?

It's an interesting idea, but I guess I don't see what makes it dramatically different from Cocoa's notification center. Compare and contrast:

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're trying to say that, line-for-line, a Signals library for Cocoa is different; rather, the argument goes that it terms of coupling, it isn't as tight as delegates, but not as loose as notifications. To that end, I guess I wonder how necessary it is? I guess I can see somewhat of a need to say "this object 'A' relies heavily on 'B', but doesn't need to be coupled all that closely", but to be honest, that seems like somewhat rare situation.

At any rate, NSNotificationCenter and its ilk, as well as delegates, are pretty standard in Cocoa apps. I always use the rule of thumb that if you deviate from a standard, even a de facto standard, you should have a good reason. If you have a good reason for using neither NSNotificationCenter nor delegates, then you might have a good reason to use this Signals setup. (And as an aside, I'm hesitant to associate notifications and delegates -- they each have a role and exist for different reasons.)

It's hard to say more without a specific use case. I'm inclined to say, "Hey, it looks cool in a geeky way, but it looks like it fills a role already served by notifications." Do you have any specific use cases you could cite?

梦巷 2024-10-21 15:29:13

您对这样的解决方案有何看法?

我实在看不出有什么好处。对我来说,它似乎是目标/操作+通知的组合(对于单个通知事件,您可以有多个目标/操作,但 t/a 对是在对象本身注册的,而不是全局通知中心)。事实上,它更像是键值观察,只不过 KVO 仅限于可观察的属性。

如果您的一名实习生将其放入项目中,您会立即将其从项目中删除吗?

不,这不是代码。事实上,它看起来有点整洁。但我只是没有看到它有明显的好处。

如果你的员工已经完成实习,你会解雇他吗?

当然不是。你不会因为写出好的代码而解雇员工。

是否已经有类似但更宏伟的东西可供您使用?

如果您确实想让这变得简洁,请将 API 更改为使用块。然后你可以这样做:

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

但是,同样,你只能对对象明确“调度”的内容做出反应。如果您可以在任何任意方法调用之前和/或之后立即附加内容,那就更整洁了。如果您对此感兴趣,那么我会查看 github 上的 Aspect Objective-C

What do you think of a solution like this?

I don't really see what the benefit is. To me, it seems like a combination of target/action+notifications (you can have multiple target/actions for a single notification event, but the t/a pair is registered with the object itself as opposed to a global notification center). In fact, it's more like key-value-observing that way, except that KVO is limited to observable properties.

Would you instantly erase this from your project if one of your interns puts it in?

No. It's not bad code. In fact, it seems kinda neat. But I just don't see an obvious benefit to it.

Would you fire your employee if he already finished his internship?

Of course not. You don't fire people for writing good code.

Is there maybe already something similar yet much grander out there that you'd use instead?

If you really wanted to make this neat, change the API to use blocks instead. Then you could do:

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

Again, however, you'd be limited to reacting to stuff that the objects explicitly "dispatch". It would be much neater if you could attach stuff to immediately before and/or after any arbitrary method call. If you're interested in that, then I'd check out Aspect Objective-C on github.

分分钟 2024-10-21 15:29:13

我认为 NSNotifications 和 Delegates 之间存在差距。 KVO 拥有所有 Cocoa 中最差 API

至于 NSNotificationCenter 这里是它的一些问题:

  • 它很容易出现拼写错误
  • 很难跟踪给定对象的观察者,因此很难调试
  • 它非常冗长
  • 你只能在字典中传递通知数据,这意味着你不能使用弱引用或结构,除非你包装它们。 (参见:非常详细)
  • GCD 配合不佳:对队列的有限支持(仅块)

因此肯定需要更好的东西。

我创建了我自己的可观察类,我在每个项目中都使用它。另一种选择是使用 ReactiveCocoaRACSignal

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

As for NSNotificationCenter here are some of its problems:

  • it's prone to typos
  • it's hard to track observers of a given object, therefore hard to debug
  • it's very verbose
  • you can only pass notification data in a dictionary, which means you can't use weak references, or structs unless you wrap them. (see: very verbose)
  • doesn't play nice with GCD: limited support for queues (only blocks)

So there is definitely a need for something better.

I created my own observable class which I use on every project. Another alternative is to use ReactiveCocoa's RACSignal.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文