选择器或块在一个Objective-C库回调(Selectors or Blocks for cal

2019-07-29 21:54发布

我们正在开发一个定制EventEmitter启发在Objective-C消息系统。 对于听众提供回调,我们应要求块或选择 ,为什么?

你更愿意使用,作为一个开发者耗费了第三方库? 这似乎是最符合苹果的轨迹,指导方针和做法?

背景

我们正在开发一个全新的iOS SDK Objective-C中哪些其他第三方将使用嵌入功能集成到他们的应用程序。 我们的SDK的一个重要组成部分,需要事件监听器的通信。

有五种模式,我知道这样做在Objective-C的回调,其中三个不适合:

  • NSNotificationCenter -不能使用,因为它不保证顺序观察员将得到通知,并因为没有办法观察员,以防止其他观察者接收事件(如stopPropagation()在JavaScript中)。
  • 键-值观察 -似乎是一个很好的架构配合并不因为我们真正拥有的是信息传递,并不总是“国家”的约束。
  • 代表和数据源 -在我们的情况下,通常会有许多听众,没有一个可能理所当然地被称为委托。

而其中两个是竞争者:

  • 选择器 -在这种模式下,呼叫者提供一个选择器和被共同调用来处理事件的靶。
  • 块 -在IOS 4引入块允许功能周围没有束缚于像观察者/选择图案的物体通过。

这似乎是一个深奥的意见的问题,但我觉得有一个客观的“正确”的答案,我在Objective-C,以确定的确非常缺乏经验。 如果有更好的StackExchange网站这个问题,请通过移动它那里帮助我。

更新#1 - 2013年4月

我们选择作为指定的回调,我们的事件处理程序的手段。 我们有这样的选择在很大程度上快乐和不打算去除基于块的听众的支持。 它确实有两个显着的缺点:内存管理和设计阻抗。

内存管理

块最容易在堆栈上使用。 通过将其复制到堆上创建长寿块介绍了有趣的内存管理问题。

块,这使得在包含对象方法的调用隐含地提高self的引用计数。 假设你有对二传手name类的属性,如果你叫name = @"foo"一个块中,编译器将这种情况视为[self setName:@"foo"]并保留self ,这样它不会释放而块仍然存在。

实现一个EventEmitter是指具有长寿命的块。 为了防止隐性保留,发射器的用户需要创建一个__block参考self块,前外:

__block *YourClass this = self;
[emitter on:@"eventName" callBlock:...
   [this setName:@"foo"];...
}];

这种方法唯一的问题是, this被调用的处理之前,可能被释放。 因此,被释放时,用户必须注销他们的听众。

设计阻抗

有经验的Objective-C开发人员希望使用熟悉的模式库进行互动。 委托是一个非常熟悉的模式,等规范的开发人员期望使用它。

幸运的是,委托模式和基于块的听众不是相互排斥的。 虽然我们的发射器必须能够从许多地方(有一个代表将无法正常工作),我们仍然可以公开的接口,这将允许仿佛他们班是委托开发商与发射器进行交互处理的听众。

我们还没有实现这个,但我们可能会根据来自用户的请求。

更新#2 - 2013年10月

我不再工作对催生这一问题的项目,具有相当愉快地回到了我的JavaScript的故土。

聪明的开发商谁接手这个项目的正确决定完全退休,我们的基于块的定制EventEmitter。 而即将发布的已切换到ReactiveCocoa 。

这使他们比以前给予我们的EventEmitter库更高级别的信令模式,并允许它们封装信号处理比我们的基于块的事件处理程序或类级别的方法做了更好的内部状态。

Answer 1:

就个人而言,我讨厌使用委托。 由于客观-C是如何构成的,这真的杂波的代码,如果我要创建一个单独的对象/添加的协议只是通知你的事件之一,我必须实现5/6。 出于这个原因,我更喜欢块。

虽然他们(块)也有他们的缺点(如内存管理可能会非常棘手)。 他们是容易扩展,易于实现, 只是在大多数情况下是有意义的

而苹果的设计结构可使用所述发送者的委托方法,这仅仅是为了向后兼容。 最近苹果的API已经使用的块(例如CoreData),因为他们的Objective-C的未来。 虽然落水时,他们可能会搞乱代码,它也允许简单的“匿名委托”,这是不可能的目标C.

在虽然结束,它真的归结为:你愿意放弃以换取一些旧的,过时的多平台使用的块与委托? 委托的一个主要优点是,它保证在objc运行时的任何版本的工作,而块是一个更近的除了语言。

至于NSNotificationCenter / KVO而言,他们都是有用的,并有自己的目的,但作为代表,他们不打算使用。 也不能结果发回给发送方,并为某些情况下,这是至关重要的( -webView:shouldLoadRequest:例如)。



Answer 2:

我认为做正确的事情是同时实现,使用它作为一个客户端,看看有什么感觉最自然。 其实这两种方法的优点,它真的取决于上下文,你如何期望要使用的SDK。

选择的主要优点是简单的存储管理 - 只要客户注册和注销正确的,它并不需要担心内存泄漏。 随着块,存储管理变得复杂,这取决于块内的客户做什么。 它也更容易进行单元测试回调方法。 块可以肯定被写入可测试的 ,但它不是常见的做法,从我所看到的。

块的主要优点是灵活性 - 客户机可以方便地引用局部变量没有使他们的ivars。

所以,我认为这只是取决于使用情况 - 没有“客观正确答案”这样的通用设计问题。



Answer 3:

伟大的书面记录!

从编写大量的JavaScript的到来,事件驱动编程感觉比有来回的代表,在我个人的意见的方式清洁。

对于听众的内存管理方面,我在解决这个(迈克灰的大量绘画尝试MAKVONotificationCenter ),碎冰鸡尾酒无论是主叫方和发射器的dealloc实现( 如这里看到的顺序)两种方式安全地删除监听器。

我不能完全肯定这种做法有多安全,但这个想法是尝试它,直到它打破。



Answer 4:

关于库中的一点是,你只能在一定程度上预测,但将被如何使用。 所以你需要提供一个解决方案,那就是简单,并尽可能地开放 - 和熟悉的用户。

  • 对我来说,这一切最适合给代表团。 虽然你是正确的,它只能对监听器(委托),这意味着没有任何限制,用户可以写一个类为代表,它知道所有需要的监听器和通知他们。 当然你也可以提供登记类。 将调用所有注册对象的委托方法。
  • 块是一样好。
  • 你选择的名字被称为目标/行动,并简单但功能强大。
  • KVO似乎是一个没有最佳的解决方案为我井,因为这会削弱可能封装,或导致了如何使用图书馆的类的wrog心理模型。
  • NSNotifications很不错,告知某些事件,但用户不应该被强迫使用它们,因为它们很正规。 和你的类将无法知道,如果有一个人调整项。

在API-设计一些有用的想法: http://mattgemmell.com/2012/05/24/api-design/



文章来源: Selectors or Blocks for callbacks in an Objective-C library