0

This is strange. Whenever I enable LLVM compiler optimization (-O/-O1 or higher) I get an EXC_BAD_ACCESS error when a property on my class is accessed. The property is accessed earlier in the code flow without a problem, but at "some point" (I know, it's vague) the app crashes.

I've looked at several other Stack Overflow questions re: strict aliasing but don't see (or understand) where I may be breaking that in my code. Here's the offending class: (crash indicated near the end of the class)

#import "MessageManager.h"
#import "NSURL+Additions.h"
#import "NSString+Guid.h"

static MessageManager *_instance = NULL;

@interface MessageManager ()

@property (nonatomic, strong) NSMutableDictionary *listeners;
@property (nonatomic, strong) NSMutableDictionary *senders;

@end

@implementation MessageManager

@synthesize listeners, senders;

+ (MessageManager *) getInstance
{
    @synchronized(self)
    {
        if (self == [MessageManager class] && _instance == NULL) {
            _instance = [[self alloc] init];
            _instance.listeners = [NSMutableDictionary dictionary];
            _instance.senders = [NSMutableDictionary dictionary];
        }
    }
    return (_instance);
}

#pragma mark
#pragma mark Senders

- (void) addMessage:(Message *)message sender:(void (^) ())sender callback:(void (^) (Message *))callback
{
    [self addMessage:message sender:sender callback:callback sendImmediately:NO];
}

- (void) addMessage:(Message *)message sender:(void (^) ())sender callback:(void (^) (Message *))callback sendImmediately:(BOOL)sendNow
{
    message.id = [NSString stringWithGuid];
    [senders setObject:[NSDictionary dictionaryWithObjectsAndKeys:message, @"message", [sender copy], @"sender", nil] forKey:message.id];

    // note: callbacks use the message id and not the key, so that they remain tied to the message and not other keyed events
    if (callback) [self registerListener:callback forKey:message.id];

    if (sendNow) {
        message.isSent = YES;
        sender();
    }
}

- (void) sendAll
{
    [self sendAllForTags:nil];
}

- (void) sendAllForTags:(NSArray *)tags
{
    typedef void (^SenderBlock) ();
    [senders enumerateKeysAndObjectsUsingBlock:^(NSString *messageId, id wrapper, BOOL *stop) {

        Message *message = (Message *)[(NSDictionary *)wrapper objectForKey:@"message"];
        id sender = [(NSDictionary *)wrapper objectForKey:@"sender"];
        BOOL validTag;
        if (tags && tags.count) {
            if (message.tags.count) {
                validTag = false;
                for (NSString *tag in tags) {
                    if ([message.tags containsObject:tag]) {
                        validTag = true; // found match! ok to send.
                        break;
                    }
                }
            } else validTag = false; // tags specified, but none in message, never send
        } else validTag = true; // no tags specified, always send

        if ((!message.isSent || message.isRepeatable) && validTag) {
            message.isSent = YES;
            ((SenderBlock)sender)(); // send message!
        }
    }];
}

#pragma mark
#pragma mark Listeners

- (void) registerListener:(void (^) (Message *))listener forKey:(NSString *)key
{
    if (![listeners objectForKey:key]) {
        [listeners setObject:[NSMutableArray array] forKey:key];
    }
    [(NSMutableArray *)[listeners objectForKey:key] addObject:listener];
}

- (BOOL) handleOpenURL:(NSURL *)url
{
    Message *message = [[Message alloc] initWithJson:[[url queryParams] objectForKey:@"message"]];
    NSLog(@"%@", listeners); // <<<<----- CRASH HAPPENS HERE (or the first place in this method that "listeners" is referenced)
    NSMutableArray *keyedListeners = (NSMutableArray *)[listeners objectForKey:message.key];
    typedef void (^ListenerBlock)(Message *);
    for (ListenerBlock keyedListener in keyedListeners) {
        keyedListener(message);
    }
    return YES;
}


@end

NOTE: registerListener:forKey: is called several times BEFORE handleOpenUrl: gets called, without any error, even though it references the "listeners" property.

For context, this is part of a messaging framework I created that allows sending and receiving messages and events.

EDIT: Stack trace

0 MyApp 0x00026206 testflight_backtrace + 158
1 MyApp 0x00026e30 TFSignalHandler + 244
2 libsystem_c.dylib 0x36fc67ec _sigtramp + 48
3 MyApp 0x00024f7c -[MessageManager handleOpenURL:] (MessageManager.m:112)
4 MyApp 0x0001c29a -[WebViewController webView:shouldStartLoadWithRequest:navigationType:] (WebViewController.m:327)
5 UIKit 0x32492482 -[UIWebView webView:decidePolicyForNavigationAction:request:frame:decisionListener:] + 182
6 CoreFoundation 0x352cf7e3 __invoking___ + 67
7 CoreFoundation 0x3522a7b0 -[NSInvocation invoke] + 160
8 CoreFoundation 0x3522a3ce -[NSInvocation invokeWithTarget:] + 50
9 WebKit 0x338b1e0c -[_WebSafeForwarder forwardInvocation:] + 252
10 CoreFoundation 0x352cea82 ___forwarding___ + 666
11 CoreFoundation 0x3522964f _CF_forwarding_prep_0 + 47
12 CoreFoundation 0x352cf7e3 __invoking___ + 67
13 CoreFoundation 0x3522a7b0 -[NSInvocation invoke] + 160
14 WebCore 0x37060648 _ZL11SendMessageP12NSInvocation + 24
15 WebCore 0x37073b44 _ZL20HandleDelegateSourcePv + 80
16 CoreFoundation 0x352a0ad2 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 14
17 CoreFoundation 0x352a029e __CFRunLoopDoSources0 + 214
18 CoreFoundation 0x3529f044 __CFRunLoopRun + 652
19 CoreFoundation 0x352224a4 CFRunLoopRunSpecific + 300
20 CoreFoundation 0x3522236c CFRunLoopRunInMode + 104
21 GraphicsServices 0x3651e438 GSEventRunModal + 136
22 UIKit 0x32318e7c UIApplicationMain + 1080
23 MyApp 0x0001aa32 main (main.m:34)
24 MyApp 0x0001a9e7 start + 39
David James
  • 2,430
  • 1
  • 26
  • 35

1 Answers1

1

I think it is the blocks you are not correctly copying into the collection (listeners). Look at this question: Keep blocks inside a dictionary

There is a a very good write up on why you need to do this

Community
  • 1
  • 1
Paul de Lange
  • 10,613
  • 10
  • 41
  • 56
  • You are so right! and fortunately with ARC it only required a straightforward copy: `[(NSMutableArray *)[listeners objectForKey:key] addObject:[listener copy]];` Thanks! – David James Jun 29 '12 at 13:55