7

这可能是 iOS 9.3(发行版)中的一个可怕错误。

当添加一个观察者时,[NSUserDefaults standardUserDefaults]我注意到响应方法-observeValueForKeyPath:ofObject:change:context:被多次调用。

在下面的简单示例中,每次按下 UIButton 一次,observeValueForKeyPath 触发两次。在更复杂的示例中,它会触发更多次。它仅存在于 iOS 9.3(在 sim 卡和设备上)。

这显然会对应用程序造成严重破坏。其他人有同样的经历吗?

// ViewController.m (barebones, single view app)

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"viewDidLoad");
    [[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:@"SomeKey" options:NSKeyValueObservingOptionNew context:NULL];
}

- (IBAction)buttonPressed:(id)sender {
    NSLog(@"buttonPressed");
    [[NSUserDefaults standardUserDefaults] setInteger:1 forKey:@"SomeKey"];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    NSLog(@"observeValueForKeyPath: %@", keyPath);
} 
4

3 回答 3

5

是的,我也遇到了这个问题,这似乎是一个错误,下面是我目前正在使用的一个快速解决方法,直到这个问题得到修复。我希望它有帮助!

还要澄清一下,由于 iOS 7 KVO 与 NSUserDefaults 配合得很好,而且正如 Matt 所说,它显然是可观察的键值,它在 iOS 9.3 SDK 的 NSUserDefaults.h 中明确编写:“可以使用 Key-Value 观察 NSUserDefaults观察其中存储的任何密钥。”

#include <mach/mach.h>
#include <mach/mach_time.h>

@property uint64_t newTime;
@property uint64_t previousTime;
@property NSString *previousKeyPath;

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    //Workaround for possible bug in iOS 9.3 SDK that is causing observeValueForKeyPath to be called multiple times.
    newTime = mach_absolute_time();
    NSLog(@"newTime:%llu", newTime);
    NSLog(@"previousTime:%llu", previousTime);

    //Try to avoid duplicate calls
    if (newTime > (previousTime + 5000000.0) || ![keyPath isEqualToString:previousKeyPath]) {
        if (newTime > (previousTime + 5000000.0)) {
            NSLog(@"newTime > previousTime");
            previousTime = newTime;
            NSLog(@"newTime:%llu", newTime);
            NSLog(@"previousTime:%llu", previousTime);
        }
        if (![keyPath isEqualToString:previousKeyPath]) {
            NSLog(@"new keyPath:%@", keyPath);
            previousKeyPath = keyPath;
            NSLog(@"previousKeyPath is now:%@", previousKeyPath);
        }
        //Proceed with handling changes
        if ([keyPath isEqualToString:@“MyKey"]) {
            //Do something
        }
    }
}
于 2016-04-04T09:04:32.307 回答
2

当添加一个观察者时,[NSUserDefaults standardUserDefaults]我注意到响应方法-observeValueForKeyPath:ofObject:change:context:被多次调用

这是一个已知问题, (Apple)报告为在 iOS 11 和 macOS 10.13 中修复。

于 2017-08-02T15:08:34.750 回答
0

为 MacOS (10.13) 添加这个答案,它肯定存在获取 NSUserDefault Keys 的 KVO 的多个通知的错误,并且还解决了弃用问题。最好使用经过纳秒的计算来获取您正在运行的机器。这样做:

#include <mach/mach.h>
#include <mach/mach_time.h>
static mach_timebase_info_data_t _sTimebaseInfo;

uint64_t  _newTime, _previousTime, _elapsed, _elapsedNano, _threshold;
NSString  *_previousKeyPath;

-(BOOL)timeThresholdForKeyPathExceeded:(NSString *)key thresholdValue:(uint64_t)threshold
{
   _previousTime = _newTime;
   _newTime = mach_absolute_time();

    if(_previousTime > 0) {
        _elapsed = _newTime - _previousTime;
        _elapsedNano = _elapsed * _sTimebaseInfo.numer / _sTimebaseInfo.denom;
    }

    if(_elapsedNano > threshold || ![key isEqualToString:_previousKeyPath]) {
        if(![key isEqualToString:_previousKeyPath]) _previousKeyPath = key;
            return YES;
        }
        return NO;
    }
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if(![self timeThresholdForKeyPathExceeded:keyPath thresholdValue:5000000]) return;  // Delete this line of MacOS bug ever fixed
    }
    // Else this is the KeyPath you are looking for Obi Wan, process it.
}

这是基于此Apple Doc的清单 2 : https ://developer.apple.com/library/content/qa/qa1398/_index.html

于 2017-10-02T19:04:03.513 回答