0

Important update: I found out that most part of my question was based on a false premise (see my answer below). Notifications actually got to the receiver, they just got there too fast. (Although, it still doesn't explain why the behavior with breakpoint and without it was different.)

I'm developing the app that calculates the hashes of files given to it. The calculation takes place in SHHashComputer. It's an abstract class (well, intended to be abstract, as there are no abstract classes in Objective C) that takes the file path and creates an NSInvocationOperation. It, in turn, calls the method (void)computeAndSendHash, which uses the file path saved in the object to compute hash and sends it as notification. The actual computing takes place in (NSString*)computeHash method that child classes need to override.

Here's SHHashComputer.m:

- (NSString*)computeHash {

    return [NSString stringWithFormat:@"unimplemented hash for file %@", self.path];
}

- (void)computeAndSendHash {

    NSString *result = [self computeHash];
    NSString *notificationName = [NSString stringWithFormat:@"%@%@",
                                  gotResultNotification,
                                  self.hashType];
    [[NSNotificationCenter defaultCenter] postNotificationName:notificationName
                                                        object:result];
    self.operation = nil;

}

And here's SHMD5Computer.m (the child class of SHHashComputer):

- (NSString*)computeHash {

    return @"MD5 test"; // it actually doesn't matter what it returns

}

I won't bother you with the receivers of notification. Let's just say that as long as I comment out the computeHash method in SHMD5Computer.m everything works just fine: the notification with text "unimplemented ..." is received & displayed in GUI. But if I don't — then it gets really interesting.

If I don't set up any breakpoints, the notification just never comes. However, if I set up a breakpoint at the declaration of computeHash in SHMD5Computer.h and then step over until the line 'self.operation = nil', and continue execution at that point, the notification gets to destination. If I don't stop there, the debugger suddenly switches to the state as if it isn't debugging anything, and the app freezes.

I don't think that 'WTF' is a good form for a question here, so let me put it this way: am I missing something? Are there errors in my code? What can cause this type of behavior in xcode? How can I fix this?

(If you'll want to get all my code to reproduce it, I'll gladly give it to you.)

More experiments:

  1. If I continute execution exactly after stopping at breakpoint, the application encounters EXC_BAD_ACCESS error in the code that receives the notification, at the last line:

    id newResult = [newResultNotification object];
    
    if (newResult == nil)
        [NSException raise:@"No object"
                    format:@"Expected object with notification!"];
    else if (![newResult isKindOfClass:[NSString class]])
        [NSException raise:@"Not NSString"
                    format:@"Expected NSString object!"];
    else
        self.result = (NSString*) newResult;
    
    [self.textField setStringValue:self.result];
    
  2. When I tried to reproduce the previous experiment, something even stranger happenned. In my debug setup, I have two hash computer objects: one SHMD5HashComputer (which we're talking about), and one SHHashComputer (which, of course, produces the "unimpemented" hash). In all previous experiments, as long as app didn't crash, the notification form SHHashComputer always successfully arrived. But in this case, both notifications didn't arrive, and the app didn't crash. (All the steps are exactly the same as in previous one).

  3. As Josh Caswell pointer out in the comments, I wasn't using the notifications correctly. I should've sent the object itself as notification object, as described in documentation. I fixed that, and I'm getting exactly the same results. (Which means that I fixed it correctly, because sometimes the notifications work correctly, and also that it wasn't the problem).

More updates:

The notification that I'm sending should arrive at SHHashResultViewController. That's how I create it and register for notification:

- (id)initWithHashType:(NSString *)hashType {

    self = [self initWithNibName:@"SHHashResultView" bundle:[NSBundle mainBundle]];
    if (self) {
        [self setHashType:hashType];
    }

    return self;
}

- (void)setHashType:(NSString *)hashType {

    [self.label setStringValue:[NSString stringWithFormat:@"%@:", hashType]];

    _hashType = hashType;
    NSString *notificationName = [NSString stringWithFormat:@"%@%@",
                                  gotResultNotification,
                                  _hashType];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(gotResult:)
                                                 name:notificationName
                                               object:nil];
}
Max Yankov
  • 12,551
  • 12
  • 67
  • 135
  • What, where, and how are you registering to receive the notification? The `object` argument to `postNotification:` should not be `result`, it should be whatever's posting the notification. Use the `userInfo` slot for arbitrary/context info. – jscs Jun 10 '12 at 22:58
  • Added details about it, will go look at postNotification docs once more. – Max Yankov Jun 10 '12 at 23:03
  • @JoshCaswell, you was absolutely right, I wasn't using notifications right. However, I changed the code to the right usage of notifications, and I still get exactly the same results. – Max Yankov Jun 10 '12 at 23:11
  • Protocols are what Objective-C developers use in place of abstract classes. – dreamlax Jun 11 '12 at 00:24

1 Answers1

0

Actually, the question was based on a false premise. I thought that notification never came through because I never saw the information displayed in the GUI; however, my error was in the code of controllers (not published there) which made possible the situation in which the GUI first got results of hash calculation and only after that got information about a new input — which resulted in deleting all the text and activating progress animation.

Max Yankov
  • 12,551
  • 12
  • 67
  • 135