KVO通知的间歇性崩溃

时间:2013-05-14 17:37:14

标签: ios objective-c reactive-programming reactive-cocoa

这是发生了什么:我有一个单例监控设备的事件存储库以进行更改。我有一个名为events的属性,我已将其包含在eventsSignal RACSignal中。

_eventsSignal = [RACAble(self.events) startWith:nil];

当应用程序完成启动时,它会使用requestAccessToEntityType:completion:提示用户访问日历(标准方法)。完成块在后台队列上执行,因此我将调度回主队列:

-(void)promptForAccess {
  [_store requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
      dispatch_async(dispatch_get_main_queue(), ^{
          [self willChangeValueForKey:EKEventManagerAccessibleKeyPath];
          _accessible = granted;
          [self didChangeValueForKey:EKEventManagerAccessibleKeyPath];

          if (_accessible) {
              // load events
              [_store reset];
              [self refresh];
          }
      });
  }];
}

[self refresh]的调用会从事件存储中加载新事件,然后调用

[self didChangeValueForKey:@"events"];

在这一行,应用程序崩溃了。

-[__NSCFString sourceType]: unrecognized selector sent to instance 0x200d6c80

完整的堆栈跟踪如下。我已经尝试删除调度调用,总是在主线程调度程序上调度_eventSignal,并查看我正在订阅事件信号的所有地方(它看起来都很好)。我可能遗失的任何东西?

修改: 我已经将问题与以下代码隔离开来了。如果nextEventSignal完全订阅,则崩溃发生的时间大约是一半。如果我从combineLatest:reduce调用中删除第二个信号,那么它不会崩溃。

RACSignal *nextEventSignal = [[RACSignal combineLatest:@[eventManager.eventsSignal, eventManager.nextEventSignal, timerSignal] reduce:^id (NSArray *eventArray, EKEvent *nextEvent, NSDate *fireDate){
    NSArray *filteredArray = [[eventArray filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL (EKEvent *event, NSDictionary *bindings) {
        return [event.endDate isLaterThanDate:[NSDate date]] && !event.isAllDay;
    }]] sortedArrayUsingComparator:^NSComparisonResult (id obj1, id obj2) {
        return [[obj1 startDate] compare:[obj2 startDate]];
    }];

    if (filteredArray.count == 0) {
        if (nextEvent.isAllDay) {
            return  nil;
        }
        else {
            return nextEvent;
        }
    } else {
        return filteredArray[0];
    }
}] throttle:0.25f];

这是崩溃日志。

thread #1: tid = 0x2503, 0x3b40f944 libobjc.A.dylib`objc_exception_throw, stop reason = breakpoint 2.1
frame #0: 0x3b40f944 libobjc.A.dylib`objc_exception_throw
frame #1: 0x33717f30 CoreFoundation`-[NSObject(NSObject) doesNotRecognizeSelector:] + 184
frame #2: 0x3371664c CoreFoundation`___forwarding___ + 392
frame #3: 0x3366e208 CoreFoundation`__forwarding_prep_0___ + 24
frame #4: 0x33e0442c EventKit`-[EKCalendar type] + 68
frame #5: 0x33e49e10 EventKit`-[EKCalendar description] + 84
frame #6: 0x33f7d204 Foundation`_NSDescriptionWithLocaleFunc + 88
frame #7: 0x336f5430 CoreFoundation`__CFStringAppendFormatCore + 11160
frame #8: 0x3366c8a2 CoreFoundation`_CFStringCreateWithFormatAndArgumentsAux + 74
frame #9: 0x33f7ccfc Foundation`+[NSString stringWithFormat:] + 60
frame #10: 0x33e5427c EventKit`-[EKCalendarItem description] + 228
frame #11: 0x33e4ccae EventKit`-[EKEvent description] + 46
frame #12: 0x336a35d0 CoreFoundation`-[NSArray descriptionWithLocale:indent:] + 680
frame #13: 0x33f7d1f0 Foundation`_NSDescriptionWithLocaleFunc + 68
frame #14: 0x336f5430 CoreFoundation`__CFStringAppendFormatCore + 11160
frame #15: 0x3366c8a2 CoreFoundation`_CFStringCreateWithFormatAndArgumentsAux + 74
frame #16: 0x33f7ccfc Foundation`+[NSString stringWithFormat:] + 60
frame #17: 0x0010102a Upcoming`-[RACTuple description](self=0x1ed6dee0, _cmd=0x391cb2ce) + 182 at RACTuple.m:62
frame #18: 0x33f7d204 Foundation`_NSDescriptionWithLocaleFunc + 88
frame #19: 0x336f5430 CoreFoundation`__CFStringAppendFormatCore + 11160
frame #20: 0x3366c8a2 CoreFoundation`_CFStringCreateWithFormatAndArgumentsAux + 74
frame #21: 0x33f873de Foundation`-[NSString initWithFormat:arguments:] + 26
frame #22: 0x000f9e50 Upcoming`-[RACStream setNameWithFormat:](self=0x1eda2a70, _cmd=0x0012e18b, format=0x001ad984) + 436 at RACStream.m:56
frame #23: 0x000f26a2 Upcoming`+[RACSignal(self=0x001ab424, _cmd=0x0012e129, value=0x1ed6dee0) return:] + 222 at RACSignal.m:165
frame #24: 0x000fa926 Upcoming`__29-[RACStream(.block_descriptor=0x1edf57b0, value=0x1ed52170) map:]_block_invoke + 86 at RACStream.m:91
frame #25: 0x000fa13c Upcoming`__36-[RACStream(.block_descriptor=0x200f3970, value=0x1ed52170, stop=0x2fdc1580) flattenMap:]_block_invoke_2 + 44 at RACStream.m:72
frame #26: 0x000f39f4 Upcoming`__29-[RACSignal(.block_descriptor=0x200f3b70, x=0x1ed52170) bind:]_block_invoke178 + 56 at RACSignal.m:243
frame #27: 0x000ffbb6 Upcoming`-[RACSubscriber sendNext:](self=0x200f3a90, _cmd=0x00124ec2, value=0x1ed52170) + 294 at RACSubscriber.m:69
frame #28: 0x000deb04 Upcoming`__43-[RACSignal(.block_descriptor=0x200f3cc0) combineLatestWith:]_block_invoke_2 + 304 at RACSignal+Operations.m:462
frame #29: 0x000ded76 Upcoming`__43-[RACSignal(.block_descriptor=0x200f3df0, x=0x20196dc0) combineLatestWith:]_block_invoke463 + 282 at RACSignal+Operations.m:469
frame #30: 0x000ffbb6 Upcoming`-[RACSubscriber sendNext:](self=0x200f3d30, _cmd=0x00124ec2, value=0x20196dc0) + 294 at RACSubscriber.m:69
frame #31: 0x000f3694 Upcoming`__29-[RACSignal(.block_descriptor=0x1eda3610, x=0x20196dc0) bind:]_block_invoke_2157 + 76 at RACSignal.m:222
frame #32: 0x000ffbb6 Upcoming`-[RACSubscriber sendNext:](self=0x1edea190, _cmd=0x00124ec2, value=0x20196dc0) + 294 at RACSubscriber.m:69
frame #33: 0x000f2746 Upcoming`__31+[RACSignal(.block_descriptor=0x1edc5dd0, subscriber=0x1edea190) return:]_block_invoke + 106 at RACSignal.m:166
frame #34: 0x000f6a30 Upcoming`__37-[RACSignal(.block_descriptor=0x2fdc1c20) subscribe:]_block_invoke300 + 80 at RACSignal.m:386
frame #35: 0x00100aca Upcoming`-[RACSubscriptionScheduler schedule:](self=0x200c0e00, _cmd=0x0012de2e, block=0x2fdc1c20) + 542 at RACSubscriptionScheduler.m:40
frame #36: 0x000f650c Upcoming`-[RACSignal(self=0x2019a790, _cmd=0x0012de00, subscriber=0x1edea190) subscribe:] + 1300 at RACSignal.m:388
frame #37: 0x000f74ec Upcoming`-[RACSignal(self=0x2019a790, _cmd=0x0012f167, nextBlock=0x2fdc1ee4, errorBlock=0x2fdc1ec8, completedBlock=0x2fdc1ea8) subscribeNext:error:completed:] + 1124 at RACSignal.m:419
frame #38: 0x000f3528 Upcoming`__29-[RACSignal(.block_descriptor=0x200f3fe0, signal=0x2019a790) bind:]_block_invoke154 + 636 at RACSignal.m:230
frame #39: 0x000f3a22 Upcoming`__29-[RACSignal(.block_descriptor=0x200f4130, x=0x200365e0) bind:]_block_invoke178 + 102 at RACSignal.m:246
frame #40: 0x000ffbb6 Upcoming`-[RACSubscriber sendNext:](self=0x200f4050, _cmd=0x00124ec2, value=0x200365e0) + 294 at RACSubscriber.m:69
frame #41: 0x000f3694 Upcoming`__29-[RACSignal(.block_descriptor=0x1ed08f40, x=0x200365e0) bind:]_block_invoke_2157 + 76 at RACSignal.m:222
frame #42: 0x000ffbb6 Upcoming`-[RACSubscriber sendNext:](self=0x1ed077b0, _cmd=0x00124ec2, value=0x200365e0) + 294 at RACSubscriber.m:69
frame #43: 0x000f2746 Upcoming`__31+[RACSignal(.block_descriptor=0x202adf90, subscriber=0x1ed077b0) return:]_block_invoke + 106 at RACSignal.m:166
frame #44: 0x000f6a30 Upcoming`__37-[RACSignal(.block_descriptor=0x2fdc23c0) subscribe:]_block_invoke300 + 80 at RACSignal.m:386
frame #45: 0x00100aca Upcoming`-[RACSubscriptionScheduler schedule:](self=0x200c0e00, _cmd=0x0012de2e, block=0x2fdc23c0) + 542 at RACSubscriptionScheduler.m:40
frame #46: 0x000f650c Upcoming`-[RACSignal(self=0x20021f60, _cmd=0x0012de00, subscriber=0x1ed077b0) subscribe:] + 1300 at RACSignal.m:388
frame #47: 0x000f74ec Upcoming`-[RACSignal(self=0x20021f60, _cmd=0x0012f167, nextBlock=0x2fdc2684, errorBlock=0x2fdc2668, completedBlock=0x2fdc2648) subscribeNext:error:completed:] + 1124 at RACSignal.m:419
frame #48: 0x000f3528 Upcoming`__29-[RACSignal(.block_descriptor=0x200f48e0, signal=0x20021f60) bind:]_block_invoke154 + 636 at RACSignal.m:230
frame #49: 0x000f3a22 Upcoming`__29-[RACSignal(.block_descriptor=0x200f4b40, x=0x200134a0) bind:]_block_invoke178 + 102 at RACSignal.m:246
frame #50: 0x000ffbb6 Upcoming`-[RACSubscriber sendNext:](self=0x200f4950, _cmd=0x00124ec2, value=0x200134a0) + 294 at RACSubscriber.m:69
frame #51: 0x000b8e98 Upcoming`__86+[NSObject(.block_descriptor=0x200f4db0, target=0x200c2a80, observer=0x200c2a80, change=0x200134a0) rac_signalWithChangesFor:keyPath:options:observer:]_block_invoke_2 + 96 at NSObject+RACPropertySubscribing.m:55
frame #52: 0x000c8d46 Upcoming`-[RACKVOTrampoline observeValueForKeyPath:ofObject:change:context:](self=0x200f4d90, _cmd=0x359cf919, keyPath=0x200c9340, object=0x200c2a80, change=0x200134a0, context=0x001b4444) + 542 at RACKVOTrampoline.m:97
frame #53: 0x340095f2 Foundation`NSKVONotify + 34
frame #54: 0x34007b46 Foundation`-[NSKeyValueObservance observeValueForKeyPath:ofObject:change:context:] + 310
frame #55: 0x33fa3b84 Foundation`NSKeyValueNotifyObserver + 272
frame #56: 0x33fa37dc Foundation`NSKeyValueDidChange + 336
frame #57: 0x33f7dcba Foundation`-[NSObject(NSKeyValueObserverNotification) didChangeValueForKey:] + 94
frame #58: 0x0005a732 Upcoming`-[EKEventManager loadEvents](self=0x200c2a80, _cmd=0x00126d19) + 2834 at EKEventManager.m:216
frame #59: 0x000591ac Upcoming`-[EKEventManager refresh](self=0x200c2a80, _cmd=0x33e9fe1c) + 64 at EKEventManager.m:82
frame #60: 0x000590d6 Upcoming`__33-[EKEventManager promptForAccess]_block_invoke_2(.block_descriptor=0x2005d020) + 246 at EKEventManager.m:74
frame #61: 0x3b829792 libdispatch.dylib`_dispatch_call_block_and_release + 10
frame #62: 0x3b8295da libdispatch.dylib`_dispatch_client_callout + 22
frame #63: 0x3b82ce44 libdispatch.dylib`_dispatch_main_queue_callback_4CF + 228
frame #64: 0x336e81b0 CoreFoundation`__CFRunLoopRun + 1288
frame #65: 0x3365b23c CoreFoundation`CFRunLoopRunSpecific + 356
frame #66: 0x3365b0c8 CoreFoundation`CFRunLoopRunInMode + 104
frame #67: 0x3723a33a GraphicsServices`GSEventRunModal + 74
frame #68: 0x355772b8 UIKit`UIApplicationMain + 1120
frame #69: 0x0003e074 Upcoming`main(argc=1, argv=0x2fdc3d20) + 116 at main.m:15
frame #70: 0x3b83cb20 libdyld.dylib`start + 4

1 个答案:

答案 0 :(得分:1)

问题结果是我在重置商店后持有对EventKit对象的引用。当商店重置时,对象变得“无效”,我猜这意味着它们变成了悬空指针(真棒)。第一个键发生更改时combineLatest:reduce:失败,因为第二个信号的最新值无效。在重置商店之前向这两个信号发送nil似乎已经奏效。

-(void)promptForAccess {
    [_store requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self willChangeValueForKey:EKEventManagerAccessibleKeyPath];
            _accessible = granted;
            [self didChangeValueForKey:EKEventManagerAccessibleKeyPath];

            if (_accessible) {
                // need to set these to nil before resetting the store.
                [self willChangeValueForKey:EKEventManagerEventsKeyPath];
                _events = nil;
                [self didChangeValueForKey:EKEventManagerEventsKeyPath];
                [self willChangeValueForKey:EKEventManagerNextEventKeyPath];
                _nextEvent = nil;
                [self didChangeValueForKey:EKEventManagerNextEventKeyPath];

                // load events
                [_store reset];
                [self refresh];
            }
        });
    }];
}