-[NSWindowController window] retaining window when NSWindowController initialized with window? - cocoa

In an application (OS X 10.6.7) I have a NSWindowController subclass which is initialized with -[NSWindowController initWithWindow:]—i.e., I have already created the window in code; I'm not loading it from a nib.
Normally, I refer to the window in my NSWindowController subclasses with [self window]. But in this case, every time I send [self window], the window gets retained, so I end up leaking quite a lot.
Is this intended behavior? For the moment I've worked around it by just storing the window in an instance variable in the init method and never sending [self window].
I am pretty sure this is not happening because NSWindowController is trying to load the window: -loadWindow does not retain the window and -isWindowLoaded returns YES:
(gdb) set $window = (id)[self window]
Current language: auto; currently objective-c
(gdb) p (int)[$window retainCount]
$1 = 3
(gdb) p (BOOL)[self isWindowLoaded]
$2 = 1 '\001'
(gdb) call (void)[self loadWindow]
(gdb) p (int)[$window retainCount]
$3 = 3
(gdb) p (int)[[self window] retainCount]
$4 = 4
(gdb) p (int)[[self window] retainCount]
$5 = 5

-[NSWindowController window] retaining the window is fine; the issue seems to be related to autorelease pools.
window = [[NSWindow alloc] initWithContentRect:NSMakeRect(100, 100, 200, 200)
styleMask:NSTitledWindowMask
backing:NSBackingStoreBuffered
defer:NO];
NSWindowController *controller = [[NSWindowController alloc] initWithWindow:window];
[window setTitle:#"testing"];
[window makeKeyAndOrderFront:nil];
[window release];
NSLog(#"[window retainCount]: %d", [window retainCount]);
[controller window];
[controller window];
[controller window];
NSLog(#"[window retainCount]: %d", [window retainCount]);
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[controller window];
[controller window];
[controller window];
NSLog(#"[window retainCount]: %d", [window retainCount]);
[pool drain];
NSLog(#"[window retainCount]: %d", [window retainCount]);
The output is:
2011-06-12 19:26:52.337 window[5517:a0b] [window retainCount]: 1
2011-06-12 19:26:52.339 window[5517:a0b] [window retainCount]: 4
2011-06-12 19:26:52.340 window[5517:a0b] [window retainCount]: 7
2011-06-12 19:26:52.340 window[5517:a0b] [window retainCount]: 4
The problem was that I forgot to create a pool when doing Cocoa stuff in a Carbon event handler (InstallApplicationEventHandler). This matches the context of the thread I linked to.
Ordinarily I see an exception when there's no autorelease pool present, so I'm guessing there is simply a pool in place that never gets drained.

Related

How to read all remaining output of readInBackgroundAndNotify after NSTask has ended?

I'm invoking various command line tools via NSTask. The tools may run for several seconds, and output text constantly to stdout. Eventually, the tool will terminate on its own. My app reads its output asynchronously with readInBackgroundAndNotify.
If I stop processing the async output as soon as the tool has exited, I will often lose some of its output that hasn't been delivered by then.
Which means I have to wait a little longer, allowing the RunLoop to process pending read notifications. How do I tell when I've read everything the tool has written to the pipe?
This problem can be verified in the code below by removing the line with the runMode: call - then the program will print that zero lines were processed. So it appears that at the time the process has exited, there's already a notification in the queue that is waiting to be delivered, and that delivery happens thru the runMode: call.
Now, it might appear that simply calling runMode: once after the tool's exit may be enough, but my testing shows that it isn't - sometimes (with larger amounts of output data), this will still only process parts of the remaining data.
Note: A work-around such as making the invoked tool outout some end-of-text marker is not a solution I seek. I believe there must be some proper way to do this, whereby the end of the pipe stream is signalled somehow, and that's what I'm looking for in an answer.
Sample Code
The code below can be pasted into a new Xcode project's AppDelegate.m file.
When run, it invokes a tool that generates some longer output and then waits for the termination of the tool with waitUntilExit. If it would then immediately remove the outputFileHandleReadCompletionObserver, most of the tool's output would be missed. By adding the runMode: invocation for the duration of a second, all output from the tool is received - Of course, this timed loop is less than optimal.
And I would like to keep the runModal function synchronous, i.e. it shall not return before it has received all output from the tool. It does run in its own tread in my actual program, if that matters (I saw a comment from Peter Hosey warning that waitUntilExit would block the UI, but that would not be an issue in my case).
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[self runTool];
}
- (void)runTool
{
// Retrieve 200 lines of text by invoking `head -n 200 /usr/share/dict/words`
NSTask *theTask = [[NSTask alloc] init];
theTask.qualityOfService = NSQualityOfServiceUserInitiated;
theTask.launchPath = #"/usr/bin/head";
theTask.arguments = #[#"-n", #"200", #"/usr/share/dict/words"];
__block int lineCount = 0;
NSPipe *outputPipe = [NSPipe pipe];
theTask.standardOutput = outputPipe;
NSFileHandle *outputFileHandle = outputPipe.fileHandleForReading;
NSString __block *prevPartialLine = #"";
id <NSObject> outputFileHandleReadCompletionObserver = [[NSNotificationCenter defaultCenter] addObserverForName:NSFileHandleReadCompletionNotification object:outputFileHandle queue:nil usingBlock:^(NSNotification * _Nonnull note)
{
// Read the output from the cmdline tool
NSData *data = [note.userInfo objectForKey:NSFileHandleNotificationDataItem];
if (data.length > 0) {
// go over each line
NSString *output = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSArray *lines = [[prevPartialLine stringByAppendingString:output] componentsSeparatedByString:#"\n"];
prevPartialLine = [lines lastObject];
NSInteger lastIdx = lines.count - 1;
[lines enumerateObjectsUsingBlock:^(NSString *line, NSUInteger idx, BOOL * _Nonnull stop) {
if (idx == lastIdx) return; // skip the last (= incomplete) line as it's not terminated by a LF
// now we can process `line`
lineCount += 1;
}];
}
[note.object readInBackgroundAndNotify];
}];
NSParameterAssert(outputFileHandle);
[outputFileHandle readInBackgroundAndNotify];
// Start the task
[theTask launch];
// Wait until it is finished
[theTask waitUntilExit];
// Wait one more second so that we can process any remaining output from the tool
NSDate *endDate = [NSDate dateWithTimeIntervalSinceNow:1];
while ([NSDate.date compare:endDate] == NSOrderedAscending) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
}
[[NSNotificationCenter defaultCenter] removeObserver:outputFileHandleReadCompletionObserver];
NSLog(#"Lines processed: %d", lineCount);
}
It's quite simple. In the observer block when data.length is 0 remove the observer and call terminate.
The code will continue after the waitUntilExit line.
- (void)runTool
{
// Retrieve 20000 lines of text by invoking `head -n 20000 /usr/share/dict/words`
const int expected = 20000;
NSTask *theTask = [[NSTask alloc] init];
theTask.qualityOfService = NSQualityOfServiceUserInitiated;
theTask.launchPath = #"/usr/bin/head";
theTask.arguments = #[#"-n", [#(expected) stringValue], #"/usr/share/dict/words"];
__block int lineCount = 0;
__block bool finished = false;
NSPipe *outputPipe = [NSPipe pipe];
theTask.standardOutput = outputPipe;
NSFileHandle *outputFileHandle = outputPipe.fileHandleForReading;
NSString __block *prevPartialLine = #"";
[[NSNotificationCenter defaultCenter] addObserverForName:NSFileHandleReadCompletionNotification object:outputFileHandle queue:nil usingBlock:^(NSNotification * _Nonnull note)
{
// Read the output from the cmdline tool
NSData *data = [note.userInfo objectForKey:NSFileHandleNotificationDataItem];
if (data.length > 0) {
// go over each line
NSString *output = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSArray *lines = [[prevPartialLine stringByAppendingString:output] componentsSeparatedByString:#"\n"];
prevPartialLine = [lines lastObject];
NSInteger lastIdx = lines.count - 1;
[lines enumerateObjectsUsingBlock:^(NSString *line, NSUInteger idx, BOOL * _Nonnull stop) {
if (idx == lastIdx) return; // skip the last (= incomplete) line as it's not terminated by a LF
// now we can process `line`
lineCount += 1;
}];
} else {
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSFileHandleReadCompletionNotification object:nil];
[theTask terminate];
finished = true;
}
[note.object readInBackgroundAndNotify];
}];
NSParameterAssert(outputFileHandle);
[outputFileHandle readInBackgroundAndNotify];
// Start the task
[theTask launch];
// Wait until it is finished
[theTask waitUntilExit];
// Wait until all data from the pipe has been received
while (!finished) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.0001]];
}
NSLog(#"Lines processed: %d (should be: %d)", lineCount, expected);
}
The problem with waitUntilExit is that it doesn't always behave the way one might think. The following is mentioned in the documenation:
waitUntilExit does not guarantee that the terminationHandler
block has been fully executed before waitUntilExit returns.
It appears this is precisely the problem you are having; it's a race condition. The waitUntilExit is not waiting long enough and the lineCount variable is reached before the NSTask completes. The solution would likely be to use a semaphore or dispatch_group, although it's unclear if you want to go that route — this is not an easy problem to resolve it seems.
*I experienced a similar issue from months back that still isn't resolved unfortunately.

Sort by Double Value and not String Value

I'm currently pulling info from an sql DB where the 'cachedDist' column is set as a double. However when I pull it into my app and create my array I turn it into an String and the sort will obviously be off, 18.15 will come before 2.15. How do I fix that in my code so it will sort distance as a Double and not a String?
In Bar object.
NSString *cachedDist
#property(nonatomic,copy) NSString *cachedDist;
#synthesize cachedDist;
My while loop in the View Controller.
while (sqlite3_step(sqlStatement)==SQLITE_ROW) {
Bar * bar = [[Bar alloc] init];
bar.barName = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement,1)];
bar.barAddress = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement,2)];
bar.barCity = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 3)];
bar.barState = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 4)];
bar.barZip = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 5)];
bar.barLat = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 8)];
bar.barLong = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 9)];
if (currentLoc == nil) {
NSLog(#"current location is nil %#", currentLoc);
}else{
CLLocation *barLocation = [[CLLocation alloc] initWithLatitude:[bar.barLat doubleValue] longitude:[bar.barLong doubleValue]];
bar.cachedDist = [NSNumber numberWithDouble:[currentLoc distanceFromLocation: barLocation]/1000];
[thebars addObject:bar];
}
My sorting
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"cachedDist" ascending:YES];
sortedArray = [thebars sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]];
return sortedArray;
NSString has a method doubleValue to make this quite simple:
double cachedDistance = [cachedDistanceString doubleValue];
which you can use in a custom comparator for your sorting, or else make the property an NSNumber or double to make sorting that much easier. (I'm not sure how you are sorting...)
edit:
I re-evaluated your code, and now it looks like we are going from a double to a string to a double... we can cut out the middle-man, so to speak.
In your #prototype section, change the #property:
// #property(nonatomic,copy) NSString *cachedDist; // old way
#property(nonatomic) double cachedDist;
then assign it like this:
bar.cachedDistance = [currentLoc distanceFromLocation: barLocation]/1000;
and remove the lines which create a string from the distance (which is actually just a double).
Alternatively, if you want to be more object oriented, you can (should?) use NSNumber objects:
#property(nonatomic,copy) NSNumber *cachedDist;
...
bar.cachedDistance = [NSNumber numberWithDouble:[currentLoc distanceFromLocation: barLocation]/1000];

When will NSautorelease release this?

Okay say I have a function like below:
-(NSNumber *)calculate{
NSNumber *myNum = [[[NSNumber alloc]initWithInt:5] autorelease];
return myNum;
}
When will myNum be released? Will whenever I call calculate, myNum will be created and added to the stack?
Also say I have a property like:
#property (nonatomic, retain) NSMutableArray *inputsArr;
and I synthesized it as:
#synthesize inputsArr = _inputsArr;
and I alloc and initiate it in the code of one of mu functions..
How would I go about releasing this memory? any guides to CoaCoa memory management...I can only find really confusing or obvious guides..
Thanks in advance
autorelease pools are thread local stacks -- you push and pop them. the deferred release message will be sent to the object when the pool is destroyed.
consider this:
NSAutoreleasePool * pool = [NSAutoreleasePool new];
NSNumber * n = [NSNumber numberWithDouble:1.0/17.0]; << n is autoreleased
[n self]; << OK!
[pool release]; << n is messaged release
[n self]; << BAM!
So 'when' really depends on how the autorelease pools are built -- but it's always possible to ensure your objects outlive a local pool, so this is never a restriction:
NSAutoreleasePool * pool = [NSAutoreleasePool new];
NSNumber * n = [NSNumber numberWithDouble:1.0/17.0]; << n is autoreleased
[n self]; << OK!
[n retain];
[pool release]; << n is messaged release
[n self]; << OK!
[n release];
[n self]; << BAM!
The above is what you should rely on. In some real world cases, an object may still be alive where you would expect "BAM!", but you should never rely on "well, it should have been destroyed, but it seems to work alright".
The NSApplication class sets up autorelease pools (instances of the NSAutoreleasePool class) during initialization and inside the event loop—specifically, within its initialization (or sharedApplication) and run methods. generally autorelease pool is popped at the end of the event loop, but this depends on you or the app.
If you are going to be using lots of temporary objects (autoreleased / from convenience methods) you may want to think about creating your own short-term autorelease pools to avoid temporary memory peaks.Autorelease objects are added to the latest autorelease pool to be created.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // create your own little autorelease pool
// these objects get added to the autorelease pool you created above
NSNumber *aNumber1 = [NSNumber numberWithFloat:1]; // refcount is 1, you are not owner, will be automatically released
NSNumber *aNumber2 = [NSNumber numberWithFloat:2]; // refcount is 1, you are not owner, will be automatically released
NSNumber *aNumber3 = [NSNumber numberWithFloat:3]; // refcount is 1, you are not owner, will be automatically released
NSNumber *aNumber4 = [NSNumber numberWithFloat:4]; // refcount is 1, you are not owner, will be automatically released
NSNumber *aNumber5 = [NSNumber numberWithFloat:5]; // refcount is 1, you are not owner, will be automatically released
NSNumber *aNumber6 = [NSNumber numberWithFloat:6]; // refcount is 1, you are not owner, will be automatically released
// ... do a bunch of stuff with all objects above.
...
[pool release]; // all objects added to this pool (the ones above) are released
Take a look at Memory Management with Objective C / Cocoa / iPhone.

Create a NSWindow under C++

I made a simple experiment, I wrote the following code in Xcode:
int main(int argc, char** argv)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSApplication* app = [[NSApplication alloc] init];
NSWindow* window = [[NSWindow alloc]
initWithContentRect: NSMakeRect(0, 0, 640, 480)
styleMask: NSTitledWindowMask | NSMiniaturizableWindowMask
backing: NSBackingStoreBuffered
defer: NO];
[window setTitle: #"New Window"];
[window center];
[window makeKeyAndOrderFront:nil];
[app run];
[pool release];
return 0;
}
It runs as expected, a new empty window out there, but if I compile it under terminal with command line:
$ g++ test.mm -framework Cocoa
$ ./a.out
It will breeze at [app run] without the window.
Am I doing wrong? Why it has different behaviors between Xcode and command line? Does somebody can tell me how I can achive the same behaviors in command line?
Thanks in advance.
Taking your code and compiling it works fine for me on a 10.7.2 machine... the window shows up (albeit behind my terminal window and does not appear as a running application).
If you're looking to get your app to show up as a process running in the dock, it has to be properly packaged in a .app bundle. If you do the following:
g++ test.mm -framework Cocoa
mkdir -p cli.app/Contents/MacOS
cp a.out cli.app/Contents/MacOS/cli
and then run cli.app/Contents/MacOS/cli from the command line, your process will show up as a running application.

Simulate arrow keys with 'j' and 'k' in an NSTableView?

I have an NSTableView with a number of rows. The allowsEmptySelection property is NO, so there is always a row selected. I can move up and down the tableview with the arrow keys as expected.
I'd like to also be able to move up and down with the 'j' and 'k' keys. I've looked at the Cocoa Event-Handling Guide, but can't figure out how to make these keys simulate the up and down arrow keys.
For what it's worth, here's what I'm currently doing. I'm not really 'simulating the arrow keys'. Rather, I'm just doing the behavior I want when 'j' and 'k' are pressed. It's fine, but I'm wondering if there is a better way...
- (void)keyDown:(NSEvent *)theEvent {
NSInteger row = [self.tableView selectedRow];
NSInteger numRows = [self.tableView numberOfRows];
switch ([theEvent keyCode]) {
case 38: // 'j'
if (row < numRows - 1) {
[self.tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:row+1] byExtendingSelection:NO];
}
break;
case 40: // 'k'
if (row > 0) {
[self.tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:row-1] byExtendingSelection:NO];
}
break;
default:
[super keyDown:theEvent];
}
}
Create a custom subclass of NSTableView to catch the events. Your subclass should have this method in it:
- (void)keyUp:(NSEvent *)event {
if([event keyCode] == 26) {//J
if([event modifierFlags] & NSShiftKeyMask) [self moveDownAndModifySelection:nil];
else [self moveDown:nil];
} else if([event keyCode] == 28) {//K
if([event modifierFlags] & NSShiftKeyMask) [self moveUpAndModifySelection:nil];
else [self moveUp:nil];
} else [super keyUp:event];
}
This will catch any J or K keys and tell the table view to move up or down, respectively. Also, if the shift key is pressed, this will add to the selection upwards or downwards.
If you choose to use this code, make sure you filter out if any other modifiers are pressed and pass them to super also. I did not do this to make it more readable.
Edit: How to create a false event
unichar theChar = NSUpArrowFunctionKey; //NSDownArrowFunctionKey
NSString *string = [NSString stringWithCharacters:&theChar length:1];
NSEvent *newEvent =[NSEvent keyEventWithType:NSKeyUp location:[event locationInWindow] modifierFlags:[event modifierFlags] timestamp:[event timestamp] windowNumber:[event windowNumber] context:nil/*get graphics context if you want*/ characters:string charactersIgnoringModifiers:string isARepeat:[event isARepeat] keyCode:theChar];
[super keyUp:newEvent];

Resources