在Cocoa Touch中实现Debounced / Coalesced模式,如`layoutSubviews` [英] Implement a Debounced/Coalesced Pattern in Cocoa Touch like `layoutSubviews`
问题描述
许多Cocoa Touch类利用了合并事件的设计模式。例如, UIViews
有一个方法 setNeedsLayout
,这会导致 layoutSubviews
在不久的将来被召唤。这在许多属性影响布局的情况下尤其有用。在每个属性的setter中,您可以调用 [self setNeedsLayout]
,这将确保布局将更新,但如果多个属性将阻止布局的许多(可能是昂贵的)更新一次更改,或者即使在运行循环的一次迭代中多次修改单个属性也是如此。其他昂贵的操作,如 setNeedsDisplay
和 drawRect:
一对方法遵循相同的模式。
A number of Cocoa Touch classes leverage a design pattern of coalescing events. UIViews
, for example, have a method setNeedsLayout
which causes layoutSubviews
to be called in the very near future. This is especially useful in situations where a number of properties influence the layout. In the setter for each property you can call [self setNeedsLayout]
which will ensure the layout will be updated, but will prevent many (potentially expensive) updates to the layout if multiple properties are changed at once or even if a single property were modified multiple times within one iteration of the run loop. Other expensive operations like the setNeedsDisplay
and drawRect:
pair of methods follow the same pattern.
这样实现模式的最佳方法是什么?具体来说,我想将一些依赖属性绑定到一个昂贵的方法,每次迭代需要调用一次。如果房产已经改变,则运行循环。
What's the best way to implement pattern like this? Specifically I'd like to tie a number of dependent properties to an expensive method that needs to be called once per iteration of the run loop if a property has changed.
可能的解决方案:
使用 CADisplayLink
或 NSTimer
你可以得到像这样的东西,但是两者似乎都比必要的更多参与,我不确定将它添加到许多对象(尤其是计时器)的性能影响是什么。毕竟,性能是做这样的事情的唯一原因。
Using a CADisplayLink
or NSTimer
you could get something working like this, but both seem more involved than necessary and I'm not sure what the performance implications of adding this to lots of objects (especially timers) would be. After all, performance is the only reason to do something like this.
我在某些情况下使用了这样的东西:
I've used something like this in some cases:
- (void)debounceSelector:(SEL)sel withDelay:(CGFloat)delay {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:sel object:nil];
[self performSelector:sel withObject:nil afterDelay:delay];
}
这在用户输入只应触发某个事件的情况下效果很好连续行动,或类似的事情。当我们想要确保触发事件没有延迟时,我们只想在同一个运行循环中合并调用,这似乎很笨拙。
This works great in situations where a user input should only trigger some event when a continuous action, or things like that. It seems clunky when we want to ensure there is no delay in triggering the event, instead we just want to coalesce calls within the same run loop.
推荐答案
NSNotificationQueue
只有您正在寻找的东西。请参阅 合并通知
NSNotificationQueue
has just the thing you're looking for. See the documentation on Coalescing Notifications
这是UIViewController中的一个简单示例:
Here a simple example in a UIViewController:
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(configureView:)
name:@"CoalescingNotificationName"
object:self];
[self setNeedsReload:@"viewDidLoad1"];
[self setNeedsReload:@"viewDidLoad2"];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self setNeedsReload:@"viewWillAppear1"];
[self setNeedsReload:@"viewWillAppear2"];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self setNeedsReload:@"viewDidAppear1"];
[self setNeedsReload:@"viewDidAppear2"];
}
- (void)setNeedsReload:(NSString *)context
{
NSNotification *notification = [NSNotification notificationWithName:@"CoalescingNotificationName"
object:self
userInfo:@{@"context":context}];
[[NSNotificationQueue defaultQueue] enqueueNotification:notification
postingStyle:NSPostASAP
coalesceMask:NSNotificationCoalescingOnName|NSNotificationCoalescingOnSender
forModes:nil];
}
- (void)configureView:(NSNotification *)notification
{
NSString *text = [NSString stringWithFormat:@"configureView called: %@", notification.userInfo];
NSLog(@"%@", text);
self.detailDescriptionLabel.text = text;
}
您可以签出文档并使用postingStyle来获取您想要的行为。在此示例中,使用 NSPostASAP
将为我们提供输出:
You can checkout the docs and play with the postingStyle to get the behavior you desired. Using NSPostASAP
, in this example, will give us output:
configureView called: {
context = viewDidLoad1;
}
configureView called: {
context = viewDidAppear1;
}
意味着背靠背调用 setNeedsReload
已合并。
meaning that back-to-back calls to setNeedsReload
have been coalesced.
这篇关于在Cocoa Touch中实现Debounced / Coalesced模式,如`layoutSubviews`的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!