Why using NSNotification argument - objective-c

Here is the code in main.m
[[NSNotificationCenter defaultCenter]addObserver:logger selector:#selector(zoneChange:) name:NSSystemTimeZoneDidChangeNotification object:nil];
here is the code in .h file
-(void)zoneChange:(NSNotification *)note;
Can someone tell me why zoneChange method take NSNotification as an argument?
How do we know what argument does this method take when trying to declare it to be used by the method mentioned in the main.m file above?
Also I did some research on the class reference and found out this for the selector parameter
Selector that specifies the message the receiver sends notificationObserver to notify it of the notification posting. The method specified by notificationSelector must have one and only one argument (an instance of NSNotification).
Please explain. Thanks

Imagine you have a controller that has two table views:
#interface MyController : NSViewController <NSTableViewDelegate>
#property(nonatomic,weak) IBOutlet NSTableView *tableView1;
#property(nonatomic,weak) IBOutlet NSTableView *tableView2;
#end
And controller is set as delegate for both table views. Now you want to track changes in selections of both tables. You implement tableViewSelectionDidChange: delegate method, but when it is called, how do you know which table view did change it's selection? Here comes the notification argument. It's object property points to table view that did sent this notification, and you can easily differentiate between two. Also it may contain userInfo dictionary, for example NSTableViewColumnDidResizeNotification contains a NSTableColumn and NSOldWidth keys in there.
In described case, the method responding to notification was called under delegate idiom, but in other scenarios (observed notification, target-action, etc.) you also sometimes must differentiate objects that caused a method call.
#implementation MyController
...
- (void)tableViewSelectionDidChange:(NSNotification *)notification
{
if ([notification object] == self.tableView1)
NSLog(#"selection changed in table 1");
else if ([notification object] == self.tableView2)
NSLog(#"selection changed in table 2");
else
NSLog(#"selection changed in unknown table (???)");
}
- (void)buttonDidClick:(id)sender
{
NSLog(#"some button did click. which? %#", sender);
}
#end

Well, this is what the api states
- (void)addObserver:(id)notificationObserver selector:(SEL)notificationSelector name:(NSString *)notificationName object:(id)notificationSender
where notificationSelector is a
Selector that specifies the message the receiver
sends notificationObserver to notify it of the notification posting.
The method specified by notificationSelector must have one and only
one argument (an instance of NSNotification).
So, most of the time, you would go and read Apple provided documentations to see what kind of arguments are available for your selector.
Source: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/Reference/Reference.html

Related

How does the "managedObjectContext" method work?

I'm just beginning to learn how to use Core Data. The tutorial that I'm learning from feels very helpful so far. In the part where they hook up the view controller where we want to store data to core data, they didn't explain the process of the method that does this "magic".
It's an implementation of the method managedObjectContext, so I thought it could be helpful if I post it here and you can explain to me/us what is the process that is being done here:
- (NSManagedObjectContext *) managedObjectContext
{
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
Also for some reason when I'm returning the context object it's not in regular colour when I'm returning a class object, its just white:
Thanks a bunch!
id delegate = [[UIApplication sharedApplication] delegate];
Here, we acquire a reference to the application delegate. This is an instance of the class that XCode generated for you (which should have the name [Prefix of your project]AppDelegate).
[UIApplication sharedApplication] returns a singleton object. Singleton is a design pattern which can basically be summed up as "if you call this method, it will always return the same object." So what that means here is that we are always getting the same application delegate whenever we call this in the program.
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
I'm actually kind of at a loss for why the tutorial wants you to call performSelector: here, respondsToSelector: seems a lot more appropriate, and performSelector: will raise an exception if your delegate object isn't of the right class anyway, so there's no point in guarding the actual message with an if statement.
If you ignore what seems to me to be unnecessary boilerplate, all you're doing is calling a method on the app delegate singleton. You can look it up in your project and see what it does -- judging by the context, it returns an NSManagedObjectContext class.
return context;
Note that this is not actually returning a class object, but instead a instance of NSManagedObjectContext. Therefore, the color white is to be expected.
What that code is doing is
Looking up the app delegate object. This is probably called something like AppDelegate in your project. This code finds the singleton by asking the shared UIApplication instance for its delegate.
Checking that the app delegate has a method named managedObjectContext.
If so, calling that method, and returning the result.
This is a lousy way to do it. Much better would be to declare a property on the view controller:
#property (readwrite, strong) NSManagedObjectContext *managedObjectContext;
Then assign a value to it when creating the view controller
newViewController.managedObjectContext = self.managedObjectContext;
That is, pass the managed object context around, assigning it to whatever object needs to use it. Don't assume that you can always look it up from the app delegate, because that will break if you ever change that part of your app delegate.
Xcode uses that color because it's a local variable in that method. Whether it's an object or some other type doesn't matter.

Unable to access App Delegate property

I'm trying to access a property in my app delegate from another class (something I thought would be rather simply) but I'm having troubles in doing so. My files currently look like this:
LTAppDelegate.h
#import <Cocoa/Cocoa.h>
#import "Subject.h"
#interface LTAppDelegate : NSObject <NSApplicationDelegate, NSOutlineViewDelegate, NSOutlineViewDataSource, NSMenuDelegate> {
}
#property Subject *selectedSubject;
#end
LTAppDelegate.m
#synthesize selectedSubject;
The value for selectedSubject is then set inside applicationDidFinishLaunchingin LTAppDelegate.m. Now I'm wanting to get access to this from another class that I have, which is called LTTableViewController and is setup like so:
LTTableViewController.h
#import <Foundation/Foundation.h>
#import "LTAppDelegate.h"
#import "Subject.h"
#import "Note.h"
#interface LTTableViewController : NSObject{
NSMutableArray *notesArray;
LTAppDelegate *appDelegate;
Subject *s;
}
-(IBAction)currentSubjectDetails:(id)sender;
#end
LTTableViewController.m
#import "LTTableViewController.h"
#implementation LTTableViewController
- (id)init
{
self = [super init];
if (self) {
appDelegate = ((LTAppDelegate *)[[NSApplication sharedApplication] delegate]);
s = [appDelegate selectedSubject];
NSLog(#"Test Subject: %#", [s title]);
}
return self;
}
-(IBAction)currentSubjectDetails:(id)sender{
NSLog(#"Selected Subject: %#", [s title]);
}
After inserting various NSLog() messages it would appear that the init method of LTTableViewController is called before applicationDidFinishLaunchingis called in LTAppDelegate. Based on that it makes sense that the "Test Subject" NSLog() in LTTableViewController.m init displays null; however, the 'currentSubjectDetails' method is linked to a button on the interface and when that is pressed after the app is finished loading, the NSLog() message still returns null.
Is there anything obvious I'm missing here. I feel like I'm being a little stupid and missing something really basic.
Similar issue is described here http://iphonedevsdk.com/forum/iphone-sdk-development/11537-viewcontroller-called-before-applicationdidfinishlaunching.html Adding this kind of functionality in the constructor is usually not recommended. Generally, I'd suggest using parameters and not relying on hidden dependencies as those will necessarily depend on the order of execution and you lose the help of the compiler to avoid invalid values. View controller initializers should not be used to store mutable references since view controllers are initialized automatically by predefined constructors, and you cannot pass parameters to them this way.
If you need to access the app delegate, then obtain it, perform operations on it and drop the reference. Try not to cache it, you'll very likely introduce hidden issues. I suggest you hook into the appear-disappear cycle if the viewed contents depend on any kind of current state.
Well, s does not exist, since it is set to null in init, so -currentSubjectDetails prints null. It is not a good idea to set your private variables in the constructor if they depend on other objects.
Rather, let the other objects explicitly tell your controller that it should use that Subject (e.g., treat s as a property).
Or, just query ((LTAppDelegate *)[[NSApplication sharedApplication] delegate]); every time.
-applicationDidFinishLaunching called when e.g. all nib's object initialized, so launching will be ended after construction of views related stuff. This means that constructors of nib's objects wouldn't use any other nib's objects (your delegate and controller initializing with nib, right?).
Try to use -awakeFromNib instead of constructors, I think it will called after construction of both objects.
If you are trying to avoid often calls of ((LTAppDelegate *)[[NSApplication sharedApplication] delegate]) I'll recommend to pass it as method parameter, in function stack. Cyclic references defense and some flexibility.

Test type of NSNotification

I need to check whether an object is an NSNotification. It is not enough to know if it is a subclass, as I want to differentiate between whether it is an NSNotification or a subclass of NSNotification.
So to elaborate I need to differentiate between the following:
An NSConcreteNotification
A Subclass of NSNotification (But not NSConcreteNotification)
The problem is that NSNotifications are actually NSConcreteNotifications and NSConcreteNotification is a private class so I can't use it to test against.
[object isMemberOfClass: [NSNotification class]] // returns NO in both cases
[object isKindOfClass: [NSNotification class]] // returns YES in both cases
There is no reason to subclass NSNotification the way you're describing. First, NSNotification already carries a userInfo dictionary. You can put any data you want in there. You can use category methods to read and write into that dictionary if you like (I do this all the time). For example, a very common thing I want to do is pass along some object, say the RNMessage. So I create a category that looks like this:
#interface NSNotificationCenter (RNMessage)
- (void)postNotificationName:(NSString *)aName object:(id)anObject message:(RNMessage *)message;
#end
#interface NSNotification (RNMessage)
- (RNMessage *)message;
#end
static NSString * const RNMessageKey = #"message";
#implementation NSNotificationCenter (RNMessage)
- (void)postNotificationName:(NSString *)aName object:(id)anObject message:(RNMessage *)message {
[self postNotificationName:aName object:anObject userInfo:[NSDictionary dictionaryWithObject:message forKey:RNMessageKey];
}
#end
#implementation NSNotification (RNMessage)
- (RNMessage *)message {
return [[self userInfo] objectForKey:RNMessageKey];
}
As #hypercrypt notes, you can also use associated references to attach data to any arbitrary object without creating an ivar, but with NSNotification it's much simpler to use the userInfo dictionary. It's much easier to print notification using NSLog. Easier to serialize them. Easier to copy them. Etc. Associated references are great, but they do add lots of little corner cases that you should avoid if you can get away with it.
That sounds like a really bad idea. When you first receive the notification, you already know what type it is, because it's passed as an explicit argument to a notification callback method. Consider storing the notification as a strongly typed property of another object, or inserting in a dictionary under an appropriate key if you're adding it to a collection, or passing it to other methods that don't preserve the type information to make it easier to identify later.
Creating dependencies on private API (including the names of private classes) will make your code more fragile, and much more likely to break in a future release. Obviously, one of the reasons these classes are private is to make it easier for Apple's engineers to change them as they see fit. For example, the concrete subclasses used by NSArray and NSMutableArray just changed in a recent release of the SDK.
To test id object is an NSNotification use:
[object isMemberOfClass:[NSNotification class]];`
To test if it is a NSConcreteNotifications use
[object isMemberOfClass:NSClassFromString(#"NSConcreteNotifications")];
Change the string to the name of a different class as needed...
You can then combine the two check for 'A Subclass of NSNotification (But not NSConcreteNotification'.
Either:
if ([object isMemberOfClass:NSClassFromString(#"NSConcreteNotifications")])
{
// It's a NSConcreteNotifications...
}
else if ([object isKindOfClass:[NSNotification class]])
{
// It's an NSNotification (or subclass) but not an NSConcreteNotifications
}
Or
if ([object isKindOfClass:[NSNotification class]] && ![object isMemberOfClass:NSClassFromString(#"NSConcreteNotifications")])
{ /* ... */ }
If you want to add properties to NSNotifications you should look into Associative References.
The basic idea is:
static const char objectKey;
- (id)object
{
return objc_getAssociatedObject(self, &objectKey);
}
- (void)setObject:(id)object
{
objc_setAssociatedObject(self, &objectKey, object, OBJC_ASSOCIATION_RETAIN);
}
As others have pointed out, it is a bad idea to rely on the name of a private class. If you are looking for one specific subclass, you could just explicitly check for that class.
[notification isMemberOfClass:[MyNotificationSubclass class]];
You could use multiple statements to check for multiple subclasses, but that would be a little cluttered. This method also requires changes every time you add a new class to look for. It may be better to define a readonly property which indicates whether a notification supports the feature you are looking for, so you aren't relying on the class so much as the ability of the class. You could use a category on NSNotification which simply returns NO for this property, and any subclasses which have the feature would override the method to return YES.
#interface NSNotification (MyFeature)
#property (readonly) BOOL hasMyFeature;
#end
#implementation NSNotification (MyFeature)
- (BOOL)hasMyFeature {
return NO;
}
#end
In the subclasses which support it:
- (BOOL)hasMyFeature {
return YES;
}
- (void)performMyFeature {
...
}
This would also allow you to change whether or not a notification has the feature enabled by changing a flag which is returned for hasMyFeature, and your checking code would simply be:
if(notification.hasMyFeature) [notification performMyFeature];

How to use delegate to pass data backwards/up view controller chain in Obj-C/Cocoa

I know there is plenty on the subject of delegates in the Apple dev documentation, as well as other books I have, and in resources like stackoverflow and others. But I'm still not getting it.
I recently watched the lecture on Navigation View Controllers, etc. in Stanford's CS193P Winter 2010 series, and in that lecture they talk about passing data forward on a stack of view controllers, which is easy. But they made a brief mention that you'd ideally use a delegate/protocol to pass data "backwards" (from detail view controller to list view controller, for example), but they didn't do a demo or post sample code.
I've read and searched for a sample of this exact scenario so I can wrap my head around that use of delegate/protocol, but can't find it. Here's some pseudo-code I'm playing with. Should it achieve passing the data "backwards"?
myListController : UIViewController <SetDataInParent> {
// when pushing detail controller onto stack,
// set DetailController delegate = self
}
myDetailController : UIViewController {
//header file
#protocol SetDataInParent <NSObject>
- (void)willSetValue:(*NSString);
#end
#interface myDetailController {
id <SetDataInParent> delegate;
}
#end
// class/m file
#implementation
#synthesize delegate;
- (void)willSetValue:(*NSString) {
// code here that would take argument
// from detail controller and set
// a value or text field to that
// argument in list controller
}
// send message to list controller class
- [delegate willSetValue:string];
#end
}
A delegate is simply any other Objective-C instance other than the one you are currently to which you will be sending one or more messages. The type you specified (id <SetDataInParent) reads that it's any Objective-C type that conforms to the SetDataInParent protocol (which I don't see defined in your code, but you could have it elsewhere.)
When going "backwards" typically the delegate link between the two objects is made at the time the child view is created. Therefore at the point the list view controller creates the detail view controller the former should set the delegate in the latter to self. The detail view controller then can use that delegate pointer to send messages to the list view controller, either directly via e.g., willSetValue: or indirectly (via performSelector:withObject:.) When using performSelector:withObject: it is generally a good idea to call respondsToSelector: on the delegate first to make sure you won't throw an exception should the object not respond to that message.

Objective-C Delegates: Have another class I call that parses XML. Need to know when its done externally

I have a class that calls another class to parse (NSXMLParse) from a URL. Now I would like for the class calling this one to know when it has finished, so that I can populate the UI. I am guessing a delegate would be the way to go but ive never worked with one and would need some guidance as to how this would be wired.
Thanks
NSNotification is, in my opinion, the easiest way to setup something like this.
This is a small snippet from one of my apps:
in method.m when the something I'm processing has finished
[[NSNotificationCenter defaultCenter] postNotificationName:#"GlobalTagsReady" object:nil];
in the class to take care of the notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(getTags) name:#"GlobalTagsReady" object:nil];
where #selector(getTags) is the method to be called
Basically, delegation just means giving an object a pointer back to something that it needs to tell about what it's doing. In Cocoa, this is usually handled through “protocols”, which are sort of the reverse of an interface declaration: they describe what methods an object will call on another object that “implements” the protocol. They're not, strictly speaking, necessary, particularly in a simple situation like this, but they're good practice and a good thing to know if you're going to be writing modular code.
In the parsing class's header:
// This is the protocol declaration; anything implementing the "ParsingDelegate" protocol needs to have any method signatures listed herein
#protocol ParsingDelegate
- (void)parsingDidEndWithResult:(NSDictionary *)result
#end
#interface ParsingClass : NSObjectOrWhatever
{
...
id<ParsingDelegate> _delegate;
}
...
// We'll use this property to tell this object where to send its messages
#property(nonatomic,assign) id<ParsingDelegate> delegate;
#end
In the parser's implementation:
#implementation ParsingClass
#synthesize delegate=_delegate;
// the "=_delegate" bit isn't necessary if you've named the instance variable without the underscore
...
In the method in which the parser finishes its stuff:
...
// make sure the delegate object responds to it
// assigning an object that doesn't conform to the delegate is a warning, not an error,
// but it'll throw a runtime exception if you call the method
if(self.delegate && [self.delegate respondsToSelector:#selector(parsingDidEndWithResult:)])
{
[self.delegate performSelector:#selector(parsingDidEndWithResult:) withObject:someResultObject];
}
...
In the UI class's header:
#interface SomeUIClass : NSObjectOrWhatever <ParsingDelegate>
In the UI class, wherever it sets up the parser,
parser.delegate = self;
then just implement the -parsingDidEndWithResult: method on the UI class. If you have multiple parsers running at once, you might want to extend the delegate method to pass in the parser object itself—that's kind of the Cocoa standard—but for the case you've described, this should do the trick.
You mainly got 3 ways to talk in cocoa: delegates, notifications and KVO. (cf http://alexvollmer.com/index.php/2009/06/24/cocoas-ways-of-talking/)
I assume that you are fetching the data and parsing the XML in a background thread. So whatever solution you are going to use, keep in mind to update the UI in the mainthread (using performSelectorOnMainThread for instance)