can't set both name and object arguments of addObserver method - macos

use this code
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(textChanged:) name:NSTextDidChangeNotification object:self.text3];
I can not get the notification I wanted.
But if I change the name or object to nil, it works. I wonder whether these two arguments can not be set non-nil simultaneously?

Related

KVO versus NSUndoManager

I have a property on my document class called passDescription, of type NSMutableDictionary. When I call setPassDescription:, the current value of the property is archived into an NSData instance using NSJSONSerialization. The property’s backing ivar, passDescription, is updated, and then an undo action is registered. The selector invoked by the action reconstitutes the NSData given to it and calls setPassDescription:.
Now, here’s the joker: passDescription is being observed using Key-Value Observing. Considerable experimentation and examination in Xcode’s debugger reveals that the old value and the new value are identical. (I know that this isn’t a pointer-aliasing issue, as that’s why I’m using an NSData instance. The NSData is created before I record the new value, making it independent of what it was created from.) Thus, when I press Command-Z to undo, nothing happens as the value that has just been restored is no different from the value that has been overwritten by the undo.
The only thing I can think of that may be causing this is that KVO is setting the passDescription ivar for me, before setPassDescription: gets called. Why would this be, and how could I prevent KVO from doing that? (I have confirmed that the setter isn’t being called twice. If it was, I would see double output in the debugger console.)
Here is the source for setPassDescription:.
- (void)setPassDescription:(NSDictionary *)param
{
NSLog(#"passDescription (before) = \n%#", passDescription);
NSError *error;
NSData *archivedOldValue = [NSJSONSerialization dataWithJSONObject:passDescription options:0 error:&error];
NSAssert(archivedOldValue != nil, #"Could not archive old pass description: %#", error);
NSData *blob = [NSJSONSerialization dataWithJSONObject:param options:NSJSONWritingPrettyPrinted error:&error];
if (blob == nil) #throw [NSException exceptionWithName:#"PBJSONException" reason:#"Could not serialize pass.json" userInfo:#{ #"error": error }];
[self.fileBrowserRoot fileWrapperWithName:#"pass.json"].fileContents = blob;
[passDescriptionLock lock];
[[self.undoManager prepareWithInvocationTarget:self] setPassDescriptionFromData:archivedOldValue];
passDescription = param;
[passDescriptionLock unlock];
NSLog(#"passDescription (after) = \n%#", passDescription);
// After the pass description has been set, refresh the list of build issues.
[self recheckForIssues];
}

NSPrintInfo printSettings not KVO compliant despite comment saying so in the header

the background: I'm adding a print panel accessory view to a print dialog (using addAccessoryController:), with controls binded to NSPrintInfo printSettings values so the values are saved in the print presets. I'm having troubles observing printSettings changes. I'm building with SDK 10.6, testing on OS X 10.7.
Here is a code sample that should work in my understanding, but observeValueForKeyPath: is never called:
- (void)testKVO
{
NSPrintInfo *printInfo = [NSPrintInfo sharedPrintInfo];
[printInfo addObserver:self forKeyPath:#"printSettings.foo" options:0 context:NULL];
[printInfo setValue:#"bar" forKeyPath:#"printSettings.foo"]; // observeValueForKeyPath:ofObject:change:context: not called
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(#"%s %# :: %#", _cmd, keyPath, object);
}
I also tried observing printSettings directly, with no more success, the observer method is not called either (the printSettings returned by NSPrintInfo is in fact of class NSPrintInfoDictionaryProxy):
- (void)testKVO
{
NSMutableDictionary *printSettings = [[NSPrintInfo sharedPrintInfo] printSettings];
[printSettings addObserver:self forKeyPath:#"foo" options:0 context:NULL];
[printSettings setValue:#"bar" forKey:#"foo"]; // observeValueForKeyPath:ofObject:change:context: not called
}
I double checked that my KVO system works in normal conditions and calls the observer method:
- (void)testKVO
{
NSMutableDictionary *printSettings = [NSMutableDictionary dictionary];
[printSettings addObserver:self forKeyPath:#"foo" options:0 context:NULL];
[printSettings setValue:#"bar" forKey:#"foo"]; // observeValueForKeyPath:ofObject:change:context: called at last!
}
So the question is: how can I observe printSettings modifications, especially to know when the user has chosen a print preset?
I'd also like the preview to be updated automatically with
- (NSSet *)keyPathsForValuesAffectingPreview
{
return [NSSet setWithObjects:
#"representedObject.printSettings.foo",
nil];
}
there is an easy workaround for the preview update: adding an indirection level by redeclaring the properties directly on the NSViewController itself. But for the print preset change I have no clue.
In the end, here is the comment in NSPrintInfo.h:
- (NSMutableDictionary *)printSettings;
The print info's print settings. You can put values in this dictionary to store them in any preset that the user creates while editing this print info with a print panel. Such values must be property list objects. This class is key-value coding (KVC) and key-value observing (KVO) compliant for "printSettings" so you can often bind controls in print panel accessory views directly to entries in this dictionary. You can also use this dictionary to get values that have been set by other parts of the printing system, like a printer driver's print dialog extension (the same sort of values that are returned by the Carbon Printing Manager's PMPrintSettingsGetValue() function). Other parts of the printing system often use key strings like "com.apple.print.PrintSettings.PMColorSyncProfileID" but dots like those in key strings wouldn't work well with KVC, so those dots are replaced with underscores in keys that appear in this dictionary, as in "com_apple_print_PrintSettings_PMColorSyncProfileID". You should use the same convention when adding entries to this dictionary.
Any help appreciated
Thanks
Well I found a way. There is an undocumented notification that is being sent when selecting a print preset or changing paper format, all that you have to do is add an observer:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(printInfoDidChange:) name:#"NSPrintInfoDidChange" object:nil];
That is not as straightforward as binding to printSettings keypaths, and I really don't like using an undocumented notification (almost as bad as using private API in term of maintainability) but that's the only way I could figure out to do the job.

NSNotificationCenter selector arguments

I want to call not just methods from NSNotification but the method's arguments
Something along the lines of
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playerinCombat:YES) name:#"PlayerinCombat" object:nil];
and not just
#selector(playerinCombat:)
just to use with
+ (BOOL)playerinCombat:(BOOL)flag {return flag; if (flag){NSLog(#"Player in Combat.");} if (!flag){NSLog(#"Player not in Combat.");}}
But it won't work. Any ideas?
On second thought userInfo can be used (I think)

Is there a nice way to set up KVO on retained properties?

Quite often I encounter a scenario when I want to observe changes on a retained property:
#interface AnObserver {…}
#property(retain) Foo *foo;
Now when I want to set up the observing, I need to write my own setter, repeating all the boilerplate setter code:
- (void) setFoo: (Foo*) newFoo {
if (newFoo == foo)
return;
[foo removeObserver:self forKeyPath:…];
[foo release], foo = [newFoo retain];
[foo addObserver:self forKeyPath:…];
}
This is dumb, because it pollutes the source with boilerplate code and it’s easy to miss something. Is there a better way to set up KVO on retained properties? I wish I could write something like Moose’s after hook to change the KVO after the property was changed.
In fact I realized I could watch the property itself:
[self addObserver:self forKeyPath:#"foo"…];
And then change the KVO when the property changes :-), but I do realize this is much more complicated than the hand-written setter I’d like to avoid.
Ideas?
How about using a key path? Say you want to observe changes on both the value1 and value2 properties of foo. You could use:
[self addObserver:self forKeyPath:#"foo.value1"];
[self addObserver:self forKeyPath:#"foo.value2"];
Then when those properties change, you'll get notifications.

NSTask executed only once

I'm having trouble executing different NSTask's. Same launchPath, different arguments. I have a class who's instances administer own NSTask objects and depending on arguments those instances were initialized with - dependent NSTask object is being created. I have two initializers:
// Method for finished task
- (void)taskFinished:(NSNotification *)aNotification {
[myTask release];
myTask = nil;
[self createTask];
}
// Designated initializer
- (id) init {
self = [super init];
if (self != nil) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(taskFinished:)
name:NSTaskDidTerminateNotification
object:nil];
[self createTask];
}
return self;
}
// Convenience initializer
- (id)initWithCommand:(NSString *)subCommand {
self = [self init];
if (self)
{
[self setCommand:subCommand];
}
return self;
}
And here 's the createTask method:
- (void)createTask {
// myTask is a property defined as NSTask*
myTask = [[NSTask alloc] init];
[myTask setLaunchPath:#"/usr/bin/executable"];
}
The actions are executed via selecting different rows in NSOutlineView (using PXSourceList as a wrapper):
- (void)sourceListSelectionDidChange:(NSNotification *)notification {
id sourceList = [notification object];
NSIndexSet *selection = [sourceList selectedRowIndexes];
NSString *identifier = [[sourceList itemAtRow:[selection firstIndex]] identifier];
// this way `/usr/bin/executable ${identifier}` is being created
MyCommand *command = [[MyCommand alloc] initWithSubcommand:identifier];
// this method executes [myTask launch];
[command execute]
}
The problem is that only first one gets executed. The second ones does not even trigger "click" event (via target-action). I think it could be cause of launchPath I'm trying to use, 'cause simple /bin/ls works fine. The same command in terminal has 0 return value (i.e. all is fine). Any guides or gotchas are much appreciated.
I can't comprehend why... but have read from numerous places that NSTask CAN ONLY be run once....
Using NSTask, your program can run another program as a subprocess and can monitor that program’s execution. NSTask creates a separate executable entity; unlike NSThread, it does not share memory space with the parent process.
By default, a task inherits several characteristics of its parent's environment: the current directory, standard input, standard output, standard error, and the values of any environment variables. If you want to change any of these, e.g., the current directory, you must set a new value before you launch the task. A task’s environment is established once it has launched.
An NSTask can only be run once. Subsequent attempts to run an NSTask raise an error.
If you run a task from a document in a document-based application, you should (at the very least) send the terminate message to the task instance in the cleanup code for the document. See also NSTaskTermination for more discussion.
This seems ridiculous... I will research and post back if I find any info the contradicts this source, (although it is usually reliable.)
If you care to wade the murky waters that surround PyObjC, you can easily use the subprocess mechanism of Python.. over, and over, and over. Oh yeah.

Resources