在 Cocoa Touch 中实现去抖/合并模式,如“layoutSubviews”

2024-03-20

许多 Cocoa Touch 类都利用合并事件的设计模式。UIViews,例如,有一个方法setNeedsLayout什么导致layoutSubviews在不久的将来会被调用。这在许多属性影响布局的情况下特别有用。在每个属性的设置器中,您可以调用[self setNeedsLayout]这将确保布局得到更新,但如果同时更改多个属性,或者即使在运行循环的一次迭代中多次修改单个属性,也会阻止对布局进行多次(可能昂贵的)更新。其他昂贵的操作,例如setNeedsDisplay and drawRect:两种方法遵循相同的模式。

实现这样的模式的最佳方法是什么?具体来说,我想将许多依赖属性绑定到一个昂贵的方法,如果属性发生更改,则需要在运行循环的每次迭代中调用一次。


可能的解决方案:

Using a CADisplayLink or NSTimer你可以得到像这样工作的东西,但两者似乎都比必要的更复杂,我不确定将其添加到许多对象(特别是计时器)会对性能产生什么影响。毕竟,性能是做这样的事情的唯一原因。

我在某些情况下使用过类似的东西:

- (void)debounceSelector:(SEL)sel withDelay:(CGFloat)delay {
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:sel object:nil];
    [self performSelector:sel withObject:nil afterDelay:delay];
}

在用户输入仅在连续操作或类似情况下触发某些事件的情况下,这非常有效。当我们想要确保触发事件没有延迟时,这似乎很笨拙,而我们只想在同一运行循环中合并调用。


NSNotificationQueue正是您正在寻找的东西。请参阅有关的文档合并通知 https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Notifications/Articles/NotificationQueues.html

这是 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;
}

您可以查看文档并使用 postsStyle 来获得您想要的行为。使用NSPostASAP,在这个例子中,将为我们提供输出:

configureView called: {
    context = viewDidLoad1;
}
configureView called: {
    context = viewDidAppear1;
}

这意味着背靠背调用setNeedsReload已合并。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Cocoa Touch 中实现去抖/合并模式,如“layoutSubviews” 的相关文章

随机推荐