1

This code has been around far longer than I have but I'm trying to figure it out.

We have a Singleton object called CourseManager. We also have an object called CourseSession. The CourseManager singleton is the only one who creates and retains an instance of the CourseSession.

Now, in the CourseSessions, there is an NSMutableArray workingListStack.

Some TableView is attempting to get the workingListStack like this...

self.navigationStack = [[[CourseManager sharedInstance] workingListStack] mutableCopy].

Now, CourseManager doesn't have workingListStack, CourseSession does. The way it achieves this is through a forwardInvocation function on CourseManager.

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    if ([self.session respondsToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:self.session]; //this line
    }
    else {
        [super forwardInvocation:anInvocation];
    }
}

Seems like a weird way to go about things. If anyone knows why this may have been originally created like this, I'd love to hear it.

Anyway, back to the point. The 4th line in the snippet above calls this code on CourseSession...

1    - (NSMutableArray *)workingListStack
2    {
3        @synchronized(_workingListStack)
4        {
5            if (!_workingListStack || [_workingListStack count] == 0) {
6                _workingListStack = [NSMutableArray array];
7                [_workingListStack addObject:[self alphaAndOmegaRoot]];
8            }
9            else {
10               [_workingListStack replaceObjectAtIndex:0 withObject:[self alphaAndOmegaRoot]];
11           }
12           return _workingListStack;
13        }
14    }

From Crashlytics, I'm receiving the crash on line 13. (Crashed: com.apple.main-thread EXC_BAD_ACCESS KERN_INVALID_ADDRESS 0x00000000d179bc17)

That seems like a weird line for the crash. Does this perhaps mean the _workingListStack that is being synchronized on line 3 is nil? Would you receive a crash when trying to synchronize on nil? What could cause this?

EDIT: Full Crashlytics Crash Log:

Thread : Crashed: com.apple.main-thread
0  libobjc.A.dylib                0x246c06d8 objc_release + 7
1  T                              0x186833 -[CourseSessionInternal workingListStack] (CourseSessionInternal.m:1003)
2  CoreFoundation                 0x24edecc4 __invoking___ + 68
3  CoreFoundation                 0x24e092ad -[NSInvocation invoke] +     292
4  CoreFoundation                 0x24e0cd47 -[NSInvocation invokeWithTarget:] + 50
5  T                              0x3ff103 -[CourseSessionManager forwardInvocation:] (CourseSessionManager.m:192)
6  CoreFoundation                 0x24edd5b1 ___forwarding___ + 352
7  CoreFoundation                 0x24e0cc88 _CF_forwarding_prep_0 + 24
8  T                              0x2c6537 -[StudentModuleListTableView resetView] (StudentModuleListTableView.m:48)
9  T                              0x2c7b4d __80-[StudentModuleListTableView observeValueForKeyPath:ofObject:change:context:]_block_invoke (StudentModuleListTableView.m:175)
10 libdispatch.dylib              0x24a77dd7 _dispatch_call_block_and_release + 10
11 libdispatch.dylib              0x24a77dc3 _dispatch_client_callout + 22
12 libdispatch.dylib              0x24a7c671 _dispatch_main_queue_callback_4CF + 1532
13 CoreFoundation                 0x24e9cfc5 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 8
14 CoreFoundation                 0x24e9b4bf __CFRunLoopRun + 1590
15 CoreFoundation                 0x24dedbb9 CFRunLoopRunSpecific + 516
16 CoreFoundation                 0x24ded9ad CFRunLoopRunInMode + 108
17 GraphicsServices               0x26067af9 GSEventRunModal + 160
18 UIKit                          0x290d9fb5 UIApplicationMain + 144
19 T                              0x120673 main (main.m:16)
20 libdispatch.dylib              0x24aa0873 (Missing)
chris P
  • 6,359
  • 11
  • 40
  • 84
  • Does your crash report say what methods/functions were implicated in the crash? (I mean, anything being called that's lower level than your line 13?) If I understand the "0x00000000d179bc17" part correctly, it wasn't working with nil but with something corrupt or unexpected, since it's an odd number presumably being used as an address. – Phillip Mills Feb 24 '16 at 20:15
  • @PhillipMills Added to OP – chris P Feb 24 '16 at 20:16
  • So, it looks as if there's an attempt to release some object that is no longer used after going out of scope at line 13. My best guess is that, at the point, whatever object it is has already been deallocated and its memory has been overwritten. How is `_workingListStack` declared...is it backing for a `@property`? – Phillip Mills Feb 24 '16 at 20:25

1 Answers1

0

First point - make sure that _workingListStack is not nil, when you call @synchronized(_workingListStack). I can't find this point in documentation. Instead, I found it in http://opensource.apple.com/source/objc4/objc4-437.1/runtime/objc-sync.m :

int objc_sync_enter(id obj)
{
    int result = OBJC_SYNC_SUCCESS;

    if (obj) {
        // ... 
    } else {
        // @synchronized(nil) does nothing
        if (DebugNilSync) {
            _objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
        }
        result = objc_sync_nil();
    }

done: 
    return result;
}

Simple test application also shows that no critical section created in this case.

Second point - avoid changing the locking object (_workingListStack) inside @synchronized section. See this answer for details: Changing the locking object inside @synchronized section

Community
  • 1
  • 1
Borys Verebskyi
  • 4,160
  • 6
  • 28
  • 42
  • Good point (#2). Using an actual lock object is a better strategy than `@synchronized` because the lock has a specific purpose. – Phillip Mills Feb 24 '16 at 20:27
  • Not only does changing the locking object mean that the synchronization amongst threads doesn't work, but (assuming the OP's code is built with ARC), replacing it probably deallocated it and even the one thread will break. – Ken Thomases Feb 24 '16 at 21:53