Challenge - NSNotification Center


#1

I think its correct, well seems to work OK

in main.m

[[NSNotificationCenter defaultCenter] addObserverForName:NSSystemTimeZoneDidChangeNotification
                                                          object:nil
                                                           queue:nil
                                                      usingBlock:^(NSNotification *note) {
                                                          NSLog(@"The System time zone has changed!");
                                                      }];

I then commented out the old [NSNotificationCenter defaultCenter] Declaration in main.m and the zoneChange Method in Logger.m

Let me know if it can be improved or I am totally on the wrong track

Cheers
Toddy


#2

Perfect!


#3

While this one seemed a bit intimidating at first it really wasn’t too bad. I came to the same solution as toddy2006.

[code] [[NSNotificationCenter defaultCenter]
addObserverForName:NSSystemTimeZoneDidChangeNotification
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
NSLog(@“The system time zone has changed!”);

     }];[/code]

I really like the conceptual picture that blocks paint when used with the notification center. It’s simpler to grasp than the callback methods to me.


#4

I’m struggling a little to catch the whole block thing, but as the book says i guess i just need to work with it some more. Anyways, i got to a little less-effective solution than the above:

[code] void (^theBlock)(NSNotification *note) = ^(NSNotification *note) {
NSLog(@“Time changed”);
};

    [[NSNotificationCenter defaultCenter] addObserverForName:NSSystemTimeZoneDidChangeNotification
                                                      object:nil
                                                       queue:nil
                                                  usingBlock];[/code]

It seems to work though. I would like to make a guess on what is happening, so i hope someone will correct me or let me know if i’m on the right track.

What i did above, was first to create a block variable (a variable to hold a block), which returns nothing, is called theBlock and takes a NSNotification pointer as an argument.

On the same line, i directly set up the block with what it is going to do when it is used. I do an assignment, and set up a block that takes an NSNotification pointer and runs a simply NSLog statement. I then pass this block in to my block variable, and it can hold this block as they take the same type of arguments.

= ^(NSNotification *note) { NSLog(@"Time changed"); };

When i then pass in my block variable

I say that theBlock is to be executed like a function executes. It is going on the stack, executed and then removed from the stack again.

What the other 2 above did:

^(NSNotification *note) { NSLog(@"The system time zone has changed!");
Was simply to create the block itself as the argument. Skipping the whole variable thing that i did. Kind of similar to either doing this:

Or as “i” did:

int i = 5 [NSNumber numberWithInt = i];

Let me know how i managed to grasp this the first time through. Thanks for reading! Great book! Now that i’m almost finished, off to amazon to review it :=)

Best Regards
/JBJ


#5

Seerex,

looks like you’ve got it.

Additionally, we call explicit use of a block such as ^(NSNotification *note) { NSLog(@“Notified!”); } a block literal, just as we call @“Hello” a string literal.

When you store the block into a variable, you then have a block variable.


#6

Alright Mikey, thanks a lot! :slight_smile: glad i got it.


#7

Hi there,

my solution worked, but I don’t seem to have understood the parameter “object”. The documentary says:

So I came up with this:

[[NSNotificationCenter defaultCenter] addObserverForName:NSSystemTimeZoneDidChangeNotification object:logger queue:nil usingBlock:^(NSNotification *note) { NSLog(@"Hey, somebody played around with the system clock - bastard! %@", note);

Although it works, it does not seem right, huh?

Anybody?

Happy coding

iFlash


#8

I did the same thing as iflash, and it didn’t work for me when I changed the time zone. Only when I set it to nil. I’m also a bit confused by that.


#9

I just finished the NSNotificationCenter challenge and am slowly starting to understand blocks better. To me, separating out the block code is more readable than creating the block anonymously (even if you have to type a little bit more). Here is my solution:

Created a typedef to start

Create the block and use it

//Create the block and assign it
NotificationBlock zoneChanger = ^(NSNotification *note) {
        NSLog(@"The system time zone has changed!");
};
        
//Updating to using a block instead
[[NSNotificationCenter defaultCenter] addObserverForName:NSSystemTimeZoneDidChangeNotification
                                                 object:nil
                                                  queue:nil
                                            usingBlock:zoneChanger];

My one question is the same as iflash and PowerLlama: why is the ‘object’ parameter in this case nil and not logger?

Thanks!


#10

To iflash, PowerLlama and DidItSave:

I think the reason the object is nil instead of logger can be found looking at the documentation for the method:

[quote]addObserverForName:object:queue:usingBlock:

obj
The object whose notifications you want to add the block to the operation queue.
If you pass nil, the notification center doesn’t use a notification’s sender to decide whether to add the block to the operation queue.

[/quote]

In this case, logger never “sends” a note for NSSystemTimeZoneDidChangeNotification.

If you had let’s say object A and object B, both of which are instances of a MarathonRunner, and a MarathonRunner indicates when it crosses the finish line by sending a notification to the NSNotificationCenter. Since A is special in some way (Old, first timer, female, etc.) you want their notification handled specifically, so you’d create an observer with A as the object, because you care about A’s notification specifically.

In the case of this challenge setting it to nil means that regardless of the (sender) object, respond to the notification.


#11

Once I had figured out the code, I trimmed out references to Logger and logger, and I deleted Logger.h and Logger.m. Here’s what I ended up with.


//
//  main.m
//  Callbacks

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        
        [[NSNotificationCenter defaultCenter] addObserverForName:NSSystemTimeZoneDidChangeNotification 
    object:nil
    queue:nil 
    usingBlock:^(NSNotification *note)  {
        NSLog(@"The system time zone has changed!");
    }];
         [[NSRunLoop currentRunLoop] run];
    }
    return 0;
}

Regarding whether the object should be “nil” or “logger”, I note that it was “nil” in the original Callback exercise.
“addObserverForName:NSStytemTimeZoneDidChangeNotification” replaced “addObserver:logger”. So that’s what led me to try starting fresh without a Logger object. It works, but I don’t actually understand what is meant by this:

That sentence makes my head hurt. There’s some preposition missing there, to my mind. “The object whose notifications you want to add” is clear enough, but “the block to the operation queue” doesn’t sound right. Pedagogically, this was a great exercise to make us look up the documentation, but I think I might need to buy a case of ibuprofen if this is typical. :confused:


#12

Why does setting addObserverForName to nil also appear to work?


#13

That’s explained in NSNotificationCenter Class Reference.

Also, yes, that sentence is a real head hurter.

We can probably rewrite it like this:
The object whose notifications you want to handle with the block to be added to the operation queue.


#14

Thanks ibex10.

Therefore setting addObserverForName to nil is also correct?


#15

Yes. But then you may be swamped with notifications that may not be of interest to you.


#16

Thanks ibex10.


#17

Warning! (I think) – happy to be enlightened if I got it wrong.

The Apple XCode documentation for this seems to be erroneous. It describes the addObserverForName:object:queue:usingBlock: method as:

  • (id)addObserverForName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block NS_AVAILABLE(10_6, 4_0);
    // The return value is retained by the system, and should be held onto by the caller in
    // order to remove the observer with removeObserver: later, to stop observation.

Note that the parameter for addObserverForName: is an (NSString ). I tried that with @“NSSystemTimeZoneDidChangeNotification” (an NSString), and while there were no
compiler complaints and no run-time errors, it simply didn’t register the block.

So I looked at this forum, saw that others had been successful with exactly the same code but without making it a string, rephrased that parameter as
addObserverForName:NSSystemTimeZoneDidChangeNotification
and it worked fine.


#18

I did a quick search for NSTimeZone.h, which contains the definition for NSSystemTimeZoneDidChangeNotification. It is defined as:

So, when typing NSSystemTimeZoneDidChangeNotification, it’s still a string pointer. Just a sneaky one.