本文介绍了在 iOS 和 OS X 中获取系统范围通知的状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一个代码来处理在 iOS 上打开/关闭屏幕(你可以看看 this 讨论类似问题的问题).我为这个问题包含了 OSX 标签,因为 OSX 具有相同的系统范围的通知工具.下面描述的问题是继承到通知工具(相对于 iOS 或 OSX).

有一种众所周知的注册系统范围通知的方法com.apple.springboard.hasBlankedScreen,以便在屏幕关闭或打开时接收通知.

仅供参考(这里是用于注册系统范围通知的 API):

  • notify_post、notify_check_notify_get_state 和 朋友
  • CFNotificationCenterPostNotification、CFNotificationCenterAddObserver 和 朋友(在内部使用 notify_post 等)

然而,这种方法有两个相互关联的问题:

  • 屏幕关闭和打开的通知具有相同的名称 (com.apple.springboard.hasBlankedScreen)
  • 观察者没有收到通知中的状态.

因此,我们需要实现一些不同的屏幕打开和关闭的解决方案(因为将调用相同的通知回调并且没有任何参数具有状态).

一般来说,核心问题是状态与通知回调解耦.我不知道如何优雅地处理这个问题.

我提出了两种直接的方法(每个方法都有缺陷).并寻找其他方法的想法或对该方法的改进.

计数解决方案

我们可以实现一个计数器来计算我们已经收到了多少通知,并根据此信息我们将知道它是打开还是关闭屏幕的通知(基于我们计数器的奇怪程度).

但是,它有两个缺点:

1) 在这种情况下,如果系统(由于设计时原因不明)将发送同名的额外通知,我们的逻辑将被搞砸,因为它会破坏奇怪的检查.

2) 另外,我们需要正确设置初始状态.所以在代码中的某个地方,我们会有类似的东西:

counter = getInitialState();注册通知();

在这种情况下,我们有一个竞争条件.如果系统会在我们执行 getInitialState() 之后发送通知并更改状态,但在 registerForNotification() 之前我们最终会得到错误的计数器值.

如果我们将执行以下代码:

registerForNotification();计数器 = getInitialState();

在这种情况下,我们还有另一个竞争条件.如果在我们做了 registerForNotification() 之后系统会发送通知并改变状态,但是在 getInitialState() 之前,我们会得到一个计数器,将进入通知回调并增加一个计数器(这会导致错误).

确定收到通知时的状态解决方案

在这种情况下,我们不存储任何计数器,而是在通知回调中使用 API notify_get_state 来获取当前状态.

这有它自己的问题:

1) 通知异步传送到应用程序.因此,如果您非常快速地关闭和打开屏幕,您可以在屏幕已经打开时收到这两个通知.因此,notify_check 将获得当前状态(与发送通知时的状态相比).

因此,当应用程序在通知回调中使用 notify_get_state 时,它​​将确定有两个通知屏幕已打开",而不是一个通知屏幕已关闭"和另一个屏幕已打开".

附言一般来说,所有描述的问题并非特定于屏幕开/关情况.它们对于任何具有独特状态并使用相同通知名称发送的系统范围通知都是实际的.

更新 1

我没有测试非常快速地打开/关闭屏幕并从notify_get_state()获得相同结果的场景.

但是,当我收到两个通知 com.apple.springboard.lockstate(通过 CFNotificationCenterAddObserver 订阅)并且我使用另一个 API 来获取当前设备锁定状态并收到时,我遇到了类似的情况两个通知的值相同.

所以我只是假设 notify_get_state 也会返回相同的值.但是,我认为这是有根据的猜测.对于两次调用,notify_get_state 的输入参数将相同(它不会改变).而且我认为系统不会存储应由 notify_get_state 返回的状态的 FIFO 队列.

解决方案

所以,我构建了一个非常简单的实验.我在调试器外的越狱 iOS 6.1 iPhone 5 上运行此程序.

代码

我使用以下代码构建了一个消费者应用程序:

#define EVENT "com.mycompany.bs"- (void)registerForNotifications {int 结果 = notify_register_dispatch(EVENT,&notifyToken,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0l),^(int 信息) {uint64_t 状态;notify_get_state(notifyToken, &state);NSLog(@"notify_register_dispatch() : %d", (int)state);});如果(结果!= NOTIFY_STATUS_OK){NSLog(@"注册失败 = %d", result);}CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),//centerNULL,//观察者notifyCallback,//回调CFSTR(EVENT),//事件名称NULL,//对象CFNotificationSuspensionBehaviorDeliverImmediately);}静态无效notifyCallback(CFNotificationCenterRef中心,无效*观察者,CFStringRef名称,常量无效*对象,CFDictionaryRef用户信息){uint64_t 状态;notify_get_state(notifyToken, &state);NSLog(@"notifyCallback(): %d", (int)state);}

因此,如您所见,它使用两种不同的方法来注册同一个自定义事件.我启动这个应用程序,让它注册事件,然后把它放到后台(按下主页按钮).

然后是生产者应用程序,它让我通过按下按钮生成事件:

double delayInSeconds = 0.001;dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0l);dispatch_async(q, ^(void) {notify_set_state(notifyToken, 2);notify_post(EVENT);});dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));dispatch_after(popTime, q, ^(void){notify_set_state(notifyToken, 3);notify_post(EVENT);});

结果

然后我运行生产者应用程序,大约每两秒手动生成一对事件.如您所见,生产者快速发布状态为 2 的事件,然后立即发布另一个状态为 3 的事件.因此,消费者应该打印 2 然后 3,用于 both 回调方法,如果这工作正常.它没有(正如你所担心的):

Feb 13 21:46:12 iPhone5 MyApp[1971] :notifyCallback(): 3Feb 13 21:46:12 iPhone5 MyApp[1971] <警告>:notify_register_dispatch():3Feb 13 21:46:12 iPhone5 MyApp[1971] <警告>:notify_register_dispatch():3Feb 13 21:46:12 iPhone5 MyApp[1971] <警告>:notifyCallback():3Feb 13 21:46:18 iPhone5 MyApp[1971] <警告>:notifyCallback():3Feb 13 21:46:18 iPhone5 MyApp[1971] <警告>:notify_register_dispatch():3Feb 13 21:46:18 iPhone5 MyApp[1971] <警告>:notify_register_dispatch():3Feb 13 21:46:18 iPhone5 MyApp[1971] <警告>:notifyCallback():32 月 13 日 21:46:22 iPhone5 MyApp[1971] <警告 >:notifyCallback():3Feb 13 21:46:22 iPhone5 MyApp[1971] <警告>:notify_register_dispatch():3Feb 13 21:46:22 iPhone5 MyApp[1971] <警告>:notify_register_dispatch():32 月 13 日 21:46:22 iPhone5 MyApp[1971] <警告 >:notifyCallback():3Feb 13 21:46:26 iPhone5 MyApp[1971] <警告>:notifyCallback():2Feb 13 21:46:26 iPhone5 MyApp[1971] <警告>:notify_register_dispatch():3Feb 13 21:46:26 iPhone5 MyApp[1971] <警告>:notify_register_dispatch():32 月 13 日 21:46:26 iPhone5 MyApp[1971] <警告 >:notifyCallback():32 月 13 日 21:46:30 iPhone5 MyApp[1971]<警告>:notifyCallback():32 月 13 日 21:46:30 iPhone5 MyApp[1971]<警告>:notify_register_dispatch():32 月 13 日 21:46:30 iPhone5 MyApp[1971]<警告>:notify_register_dispatch():32 月 13 日 21:46:30 iPhone5 MyApp[1971]<警告>:notifyCallback():32 月 13 日 21:46:33 iPhone5 MyApp[1971] <警告 >:notifyCallback():32 月 13 日 21:46:33 iPhone5 MyApp[1971]<警告>:notify_register_dispatch():32 月 13 日 21:46:33 iPhone5 MyApp[1971]<警告>:notify_register_dispatch():32 月 13 日 21:46:33 iPhone5 MyApp[1971] <警告 >:notifyCallback():32 月 13 日 21:46:36 iPhone5 MyApp[1971] <警告 >:notifyCallback():22 月 13 日 21:46:36 iPhone5 MyApp[1971]<警告>:notify_register_dispatch():32 月 13 日 21:46:36 iPhone5 MyApp[1971]<警告>:notify_register_dispatch():3Feb 13 21:46:36 iPhone5 MyApp[1971] <警告>:notifyCallback():3

我尝试将一种消费者注册方法更改为使用 CFNotificationSuspensionBehaviorCoalesce(而不是立即交付).结果:

Feb 13 21:48:17 iPhone5 MyApp[1990] :notifyCallback(): 3Feb 13 21:48:17 iPhone5 MyApp[1990] <警告>:notify_register_dispatch():3Feb 13 21:48:17 iPhone5 MyApp[1990] <警告>:notify_register_dispatch():3Feb 13 21:48:17 iPhone5 MyApp[1990] <警告>:notifyCallback():3Feb 13 21:48:20 iPhone5 MyApp[1990] <警告>:notifyCallback():2Feb 13 21:48:20 iPhone5 MyApp[1990] <警告>:notify_register_dispatch():3Feb 13 21:48:20 iPhone5 MyApp[1990] <警告>:notify_register_dispatch():3Feb 13 21:48:20 iPhone5 MyApp[1990] <警告>:notifyCallback():3Feb 13 21:48:24 iPhone5 MyApp[1990] <警告>:notifyCallback():22 月 13 日 21:48:24 iPhone5 MyApp[1990]<警告>:notify_register_dispatch():2Feb 13 21:48:24 iPhone5 MyApp[1990] <警告>:notify_register_dispatch():3Feb 13 21:48:24 iPhone5 MyApp[1990] <警告>:notifyCallback():3Feb 13 21:48:29 iPhone5 MyApp[1990] <警告>:notifyCallback():22 月 13 日 21:48:29 iPhone5 MyApp[1990]<警告>:notify_register_dispatch():32 月 13 日 21:48:29 iPhone5 MyApp[1990]<警告>:notify_register_dispatch():3Feb 13 21:48:29 iPhone5 MyApp[1990] <警告>:notifyCallback():3Feb 13 21:48:32 iPhone5 MyApp[1990] <警告>:notifyCallback():2Feb 13 21:48:32 iPhone5 MyApp[1990] <警告>:notify_register_dispatch():2Feb 13 21:48:32 iPhone5 MyApp[1990] <警告>:notify_register_dispatch():3Feb 13 21:48:32 iPhone5 MyApp[1990] <警告>:notifyCallback():32 月 13 日 21:48:35 iPhone5 MyApp[1990] <警告 >:notifyCallback():2Feb 13 21:48:35 iPhone5 MyApp[1990] <警告>:notify_register_dispatch():2Feb 13 21:48:35 iPhone5 MyApp[1990] <警告>:notify_register_dispatch():3Feb 13 21:48:35 iPhone5 MyApp[1990] <警告>:notifyCallback():3Feb 13 21:48:38 iPhone5 MyApp[1990] <Warning>:notifyCallback():2Feb 13 21:48:38 iPhone5 MyApp[1990] <警告>:notify_register_dispatch():2Feb 13 21:48:38 iPhone5 MyApp[1990] <警告>:notify_register_dispatch():3Feb 13 21:48:38 iPhone5 MyApp[1990] <警告>:notifyCallback():3Feb 13 21:48:39 iPhone5 MyApp[1990] <警告>:notifyCallback():22 月 13 日 21:48:39 iPhone5 MyApp[1990]<警告>:notify_register_dispatch():32 月 13 日 21:48:39 iPhone5 MyApp[1990]<警告>:notify_register_dispatch():3Feb 13 21:48:39 iPhone5 MyApp[1990] <警告>:notifyCallback():3

然后我尝试将 notify_register_dispatch() 消费者的队列优先级更改为 high,而不是 background 优先级.结果:

Feb 13 21:49:51 iPhone5 MyApp[2006] :notifyCallback(): 3Feb 13 21:49:51 iPhone5 MyApp[2006] <警告>:notify_register_dispatch():3Feb 13 21:49:51 iPhone5 MyApp[2006] <警告>:notify_register_dispatch():3Feb 13 21:49:51 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3Feb 13 21:49:53 iPhone5 MyApp[2006] <警告>:notifyCallback():22 月 13 日 21:49:53 iPhone5 MyApp[2006]<警告>:notify_register_dispatch():2Feb 13 21:49:53 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 32 月 13 日 21:49:53 iPhone5 MyApp[2006] <警告 >:notify_register_dispatch():3Feb 13 21:49:55 iPhone5 MyApp[2006] <警告>:notifyCallback():2Feb 13 21:49:55 iPhone5 MyApp[2006] <警告>:notify_register_dispatch():2Feb 13 21:49:55 iPhone5 MyApp[2006] <警告>:notify_register_dispatch():3Feb 13 21:49:55 iPhone5 MyApp[2006] <警告>:notifyCallback():3Feb 13 21:49:59 iPhone5 MyApp[2006] <Warning>:notifyCallback():2Feb 13 21:49:59 iPhone5 MyApp[2006] <警告>:notify_register_dispatch():2Feb 13 21:49:59 iPhone5 MyApp[2006] <警告>:notify_register_dispatch():3Feb 13 21:49:59 iPhone5 MyApp[2006] <警告>:notifyCallback():3Feb 13 21:50:01 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2Feb 13 21:50:01 iPhone5 MyApp[2006] <警告>:notify_register_dispatch():3Feb 13 21:50:01 iPhone5 MyApp[2006] <警告>:notify_register_dispatch():3Feb 13 21:50:01 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 32 月 13 日 21:50:04 iPhone5 MyApp[2006]<警告>:notify_register_dispatch():2Feb 13 21:50:04 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2Feb 13 21:50:04 iPhone5 MyApp[2006] <警告>:notify_register_dispatch():3Feb 13 21:50:04 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3Feb 13 21:50:06 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 22 月 13 日 21:50:06 iPhone5 MyApp[2006]<警告>:notify_register_dispatch():2Feb 13 21:50:06 iPhone5 MyApp[2006] <警告>:notify_register_dispatch():3Feb 13 21:50:06 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3Feb 13 21:50:09 iPhone5 MyApp[2006] <警告>:notifyCallback():2Feb 13 21:50:09 iPhone5 MyApp[2006] <警告>:notify_register_dispatch():2Feb 13 21:50:09 iPhone5 MyApp[2006] <警告>:notify_register_dispatch():3Feb 13 21:50:09 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3Feb 13 21:50:10 iPhone5 MyApp[2006] <警告>:notifyCallback():2Feb 13 21:50:10 iPhone5 MyApp[2006] <警告>:notify_register_dispatch():3Feb 13 21:50:10 iPhone5 MyApp[2006] <警告>:notify_register_dispatch():3Feb 13 21:50:10 iPhone5 MyApp[2006] <警告>:notifyCallback():3

结论 (?)

  • 正如您所怀疑的那样,存在问题,而不仅仅是 SBGetScreenLockStatus 调用.有时,消费者从未看到状态设置为 2.
  • 如果我将生产者时间延迟增加到 5 毫秒,我从未发现问题.因此,这可能只是时间非常接近的事件的问题.屏幕锁定/解锁可能没什么大不了的.显然,速度较慢的手机(iPhone
  • 静态notifyCallback() 方法似乎首先被回调,除非 GCD 回调块被放在高优先级队列中.即便如此,通常首先调用静态回调函数.很多时候,第一个回调的方法得到了正确的状态(2),而第二个没有.因此,如果您不得不忍受这个问题,您可能会选择似乎性能最好的回调机制(或者至少,在您的应用内自行设计原型).
  • 我不能说 suspensionBehavior 参数有很大的不同.也就是说,根据 iOS 发布事件的方式,他们可能会使用像 CFNotificationCenterPostNotification 可能会忽略消费者的行为请求.
  • 如果您查看此 Apple 文档,你会看到两件事.

    1. 首先,notify_set_state 不是原始 API 的一部分
    2. 其次,该文件的第一段说

达尔文通知 API 参考

这些例程允许进程交换无状态通知事件.

所以,也许我们只是想做一些与原始设计不一致的事情:(

  • 如果您还查看 Apple 的 NotificationPoster 示例,您会看到他们不使用 notify_get_statenotify_set_state 来传达状态.他们通过通知将其作为用户信息字典传递.显然,如果您正在观察 Apple 的 iOS 事件,则无法控制事件的发布方式.但是,在您可以创建生产者消费者的应用中,我会远离notify_set_state.

I am trying to write a code which will handle turning on/off screen on iOS (You can take a look at this question where similar problem is discussed). I included OSX tag for this question, because OSX has the same system wide notification facility. And the problem described below is inherit to the notifications facility (vs to iOS or OSX).

There is well known method of registering for system wide notification com.apple.springboard.hasBlankedScreen to receive notifications when the screen is turned off or on.

Just for references (here are the API which are used for registering for system wide notifications):

  • notify_post, notify_check_ notify_get_state and friends
  • CFNotificationCenterPostNotification, CFNotificationCenterAddObserver and friends (which uses notify_post and etc internally)

However, there are two interrelated problems with this approach:

  • Notifications for both screen turning off and on come with the same name (com.apple.springboard.hasBlankedScreen)
  • Observer doesn't receive a state as part of the notification.

So, as result we need to implement some solution which will differ screen turning on and off (since the same notification callback will be called and none of parameters will have a state).

Generally speaking, the core problem that the state is decoupled from notification callback. I don't see how to handle this gracefully.

I come up with two straightforward approaches (each of them are flawed). And looking for ideas of another approaches or improvements over this approach.

Counting solution

We can implement a counter which counts how many notification we already received and based on this information we will know whether it's notification for turning screen on or off (based on oddity of our counter).

However, it has two downsides:

1) In the case, if the system (for unknown in design time reason) will send additional notifications with the same name, our logic will be screwed, because it will break oddity check.

2) Also, we need to set initial state correctly. So somewhere in the code we will have something like that:

In this case we have one race condition. If system will send notification and change the state after we did getInitialState(), but before registerForNotification() we will end up with wrong counter value.

If we will do following code:

In this case we have another race condition. If system will send notification and change the state after we did registerForNotification(), but before getInitialState(), we will get a counter, will enter notification callback and will increase a counter (which will make it wrong).

Determining state when notification received solution

In this case, we don't store any counter, but rather use API notify_get_state in notification callback to get current state.

This has it's own problem:

1) Notification delivered to the application asynchronously. So, as result if you turn off and on screen really fast, you can receive both notifications when the screen was already on. So, notify_check will get a current state (vs the state at the moment when notification was send).

As result, when the application will use notify_get_state in notification callback it will determine that there was two notification "screen was turned on", instead of one notification "screen was turned off" and another "screen was turned on".

P.S. Generally speaking, all described problems aren't specific to screen on/off case. They are actual for any system wide notifications which has distinctive states and send with the same notification name.


Update 1

I didn't test exactly scenario with turning screen on/off very fast and getting the same results from notify_get_state().

However, I have kind-of similar scenario when I received two notifications com.apple.springboard.lockstate (subscribed through CFNotificationCenterAddObserver) and I used another API to get a current device locked status and received the same values for both notifications.

So it's only my assumption that notify_get_state would return same values too. However, I think it's educated guess. The input parameter for notify_get_state will be the same for two calls (it doesn't change). And I don't think that system stores FIFO queue of states which should be returned by notify_get_state.

解决方案

So, I built a very simple experiment. I ran this on a jailbroken iOS 6.1 iPhone 5, outside the debugger.

Code

I built a consumer app, with the following code:

#define EVENT "com.mycompany.bs"

- (void)registerForNotifications {
    int result = notify_register_dispatch(EVENT,
                                          &notifyToken,
                                          dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0l),
                                          ^(int info) {
                                              uint64_t state;
                                              notify_get_state(notifyToken, &state);
                                              NSLog(@"notify_register_dispatch() : %d", (int)state);
                                          });
    if (result != NOTIFY_STATUS_OK) {
        NSLog(@"register failure = %d", result);
    }
    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
                                    NULL, // observer
                                    notifyCallback, // callback
                                    CFSTR(EVENT), // event name
                                    NULL, // object
                                    CFNotificationSuspensionBehaviorDeliverImmediately);
}

static void notifyCallback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
    uint64_t state;
    notify_get_state(notifyToken, &state);
    NSLog(@"notifyCallback(): %d", (int)state);
}

So, as you see, it uses two different methods to register for the same custom event. I startup this app, let it register for the event, then put it into the background (home button press).

Then, the producer app, which lets me generate the event with a button press:

double delayInSeconds = 0.001;

dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0l);
dispatch_async(q, ^(void) {
    notify_set_state(notifyToken, 2);
    notify_post(EVENT);
});

dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, q, ^(void){
    notify_set_state(notifyToken, 3);
    notify_post(EVENT);
});

Results

I then ran the producer app, manually generating a pair of events about every two seconds. As you can see, the producer quickly posts the event with a state of 2, and then immediately posts another event with a state of 3. So, the consumer should print out 2 then 3, for both callback methods, if this is working perfectly. It does not (as you feared):

Feb 13 21:46:12 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3
Feb 13 21:46:12 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
Feb 13 21:46:12 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
Feb 13 21:46:12 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3

Feb 13 21:46:18 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3
Feb 13 21:46:18 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
Feb 13 21:46:18 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
Feb 13 21:46:18 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3

Feb 13 21:46:22 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3
Feb 13 21:46:22 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
Feb 13 21:46:22 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
Feb 13 21:46:22 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3

Feb 13 21:46:26 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 2
Feb 13 21:46:26 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
Feb 13 21:46:26 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
Feb 13 21:46:26 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3

Feb 13 21:46:30 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3
Feb 13 21:46:30 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
Feb 13 21:46:30 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
Feb 13 21:46:30 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3

Feb 13 21:46:33 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3
Feb 13 21:46:33 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
Feb 13 21:46:33 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
Feb 13 21:46:33 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3

Feb 13 21:46:36 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 2
Feb 13 21:46:36 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
Feb 13 21:46:36 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3
Feb 13 21:46:36 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3

I tried changing one consumer registration method to use CFNotificationSuspensionBehaviorCoalesce (instead of delivering immediately). Results:

Feb 13 21:48:17 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3
Feb 13 21:48:17 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
Feb 13 21:48:17 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
Feb 13 21:48:17 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3

Feb 13 21:48:20 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2
Feb 13 21:48:20 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
Feb 13 21:48:20 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
Feb 13 21:48:20 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3

Feb 13 21:48:24 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2
Feb 13 21:48:24 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 2
Feb 13 21:48:24 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
Feb 13 21:48:24 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3

Feb 13 21:48:29 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2
Feb 13 21:48:29 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
Feb 13 21:48:29 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
Feb 13 21:48:29 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3

Feb 13 21:48:32 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2
Feb 13 21:48:32 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 2
Feb 13 21:48:32 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
Feb 13 21:48:32 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3

Feb 13 21:48:35 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2
Feb 13 21:48:35 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 2
Feb 13 21:48:35 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
Feb 13 21:48:35 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3

Feb 13 21:48:38 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2
Feb 13 21:48:38 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 2
Feb 13 21:48:38 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
Feb 13 21:48:38 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3

Feb 13 21:48:39 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2
Feb 13 21:48:39 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
Feb 13 21:48:39 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3
Feb 13 21:48:39 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3

I then tried changing the queue priority of the notify_register_dispatch() consumer to high, instead of background priority. Results:

Feb 13 21:49:51 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3
Feb 13 21:49:51 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3
Feb 13 21:49:51 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3
Feb 13 21:49:51 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3

Feb 13 21:49:53 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2
Feb 13 21:49:53 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2
Feb 13 21:49:53 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3
Feb 13 21:49:53 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3

Feb 13 21:49:55 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2
Feb 13 21:49:55 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2
Feb 13 21:49:55 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3
Feb 13 21:49:55 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3

Feb 13 21:49:59 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2
Feb 13 21:49:59 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2
Feb 13 21:49:59 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3
Feb 13 21:49:59 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3

Feb 13 21:50:01 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2
Feb 13 21:50:01 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3
Feb 13 21:50:01 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3
Feb 13 21:50:01 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3

Feb 13 21:50:04 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2
Feb 13 21:50:04 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2
Feb 13 21:50:04 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3
Feb 13 21:50:04 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3

Feb 13 21:50:06 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2
Feb 13 21:50:06 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2
Feb 13 21:50:06 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3
Feb 13 21:50:06 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3

Feb 13 21:50:09 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2
Feb 13 21:50:09 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2
Feb 13 21:50:09 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3
Feb 13 21:50:09 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3

Feb 13 21:50:10 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2
Feb 13 21:50:10 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3
Feb 13 21:50:10 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3
Feb 13 21:50:10 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3

Conclusions (?)

  • There is a problem as you suspected, and it's not just with the SBGetScreenLockStatus call. Sometimes, the consumer never saw the state set to 2.
  • If I increased the producer time delay to 5 msec, I never saw the problem. So, this may only be an issue for events really close in time. Screen lock / unlock probably isn't a big deal. Obviously, slower phones (iPhone < 5) will respond differently.
  • The static notifyCallback() method seemed to get called back first, unless the GCD callback block was put on the high priority queue. Even then, usually the static callback function was called first. Many times, the first method called back got the correct state (2), while the second one did not. So, if you have to live with the problem, you might choose the callback mechanism that seemed to perform best (or at least, prototype this yourself, inside your app).
  • I can't say that the suspensionBehavior parameter made much difference. That said, depending on how iOS is posting the events, they may be using a call like CFNotificationCenterPostNotification that may ignore the consumers' behavior request.
  • If you look at this Apple document, you'll see two things.

    1. First, notify_set_state was not part of the very original API
    2. Second, the very first paragraph in that document says

So, maybe we're just trying to do something that's not consistent with the original design :(

  • If you also look at Apple's NotificationPoster example, you see that they don't use notify_get_state and notify_set_state to convey state. They pass it with the notification as a user info dictionary. Obviously, if you're observing Apple's iOS events, you have no control over how the events are posted. But, in apps where you get to create the producer and consumer, I'd stay away from notify_set_state.

这篇关于在 iOS 和 OS X 中获取系统范围通知的状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-03 04:43