在Cocoa Touch中实现Debounced / Coalesced模式,如`layoutSubviews` [英] Implement a Debounced/Coalesced Pattern in Cocoa Touch like `layoutSubviews`

查看:101
本文介绍了在Cocoa Touch中实现Debounced / Coalesced模式,如`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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆