ObjectiveCeeds › Foundation: Vom Callstack, der RunLoop und dem AutoreleasePool

Foundation: Vom Callstack, der RunLoop und dem AutoreleasePool

Von Manfred Kreß
Die RunLoop
Seite 2 von 3



Cocoa ist eine Event getriebene Architektur. Eine Applikation wird initialisiert und begibt sich danach in eine Art Warteschleife. Bei bestimmten Ereignissen wird sie aufgeweckt und eine Nachricht über das Ereignis wird an die Applikation geschickt. Das Programm klettert quasi von der Warteschleife aus den Callstack hoch bis es schließlich in der Schnittstelle zum Programmierer ankommt, z.B. mouseDown: einer NSView Instanz. Der Programmierer verarbeitet diese Nachricht und dann geht es irgendwann mit jedem Return wieder den Callstack hinunter, bis das Programm wieder in der Warteschleife angekommen ist und das nächste Ereignis in der Warteschlange dispatcht. Diese Warteschlange wird RunLoop genannt. Jede Operation in einer Cocoa Anwendung hat ihren Ursprung in einem Ereignis das in der RunLoop ausgelöst wird, sei es ein Mausevent, Daten die über eine Netzwerkverbindung ankommen oder ein Timer der feuert. ObjectiveCeeds: RunLoop Oft gibt es das Problem, das eine Anwendung nicht mehr reagiert und der Spinnball auftaucht. Das liegt daran, dass das Programm auf dem Callstack nicht mehr zur RunLoop zurückkommt, etwa wenn große Datenmengen beim Programmstart eingelesen werden müssen. Der Programmierer, dem oben auf dem Callstack der Event ausgeliefert wurde lädt die Daten in den Speicher. Das Programm kehrt dadurch eine relativ lange Zeit nicht mehr zur RunLoop zurück und eingehende Events können nicht ausgeliefert werden. Solche Probleme können über die RunLoop gelößt werden. Dabei werden die Daten in kleinen Blöcken geladen bzw. geschrieben. Die Verarbeitung dieser kleinen Blöcke geht dann relativ schnell. Ist ein Block verarbeitet, kehrt das Programm zur RunLoop zurück, der nächste Event kann verarbeitet werden oder ein weiterer Block. Cocoa bietet Klassen, die asynchron lesen und schreiben können (NSFileHandle, NSPipe), Daten über eine Netzwerkverbindung verarbeiten (NSConnection, NSStream) oder Timer feuern (NSTimer). Das Listing zeigt ein einfaches Beispiel, wie mit NSFileHandle eine große Datei eingelesen werden kann, ohne diese Aufgabe an einen Thread auszulagern. Datei mit NSFileHandle asynchron einlesen
//
//  Created by Manfred Kress.
//  Copyright 2009 ObjectiveCeeds. All rights reserved.
//

#import <Cocoa/Cocoa.h>

@interface RunLoopAppDelegate : NSObject  {
	
	NSData *data;
	NSFileHandle *handle;
}

@property (retain , readwrite) NSData *data;
@property (retain , readwrite) NSFileHandle *handle;

- (void) loadData;

- (void) finish: (NSNotification *)note;

@end
Implementation
//
//  Created by Manfred Kress.
//  Copyright 2009 ObjectiveCeeds. All rights reserved.
//

#import "RunLoopAppDelegate.h"

@implementation RunLoopAppDelegate

@synthesize data;
@synthesize handle;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {

	[self loadData];
	NSLog(@"loading data ...");

}

- (void) loadData
{

	NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
	[nc addObserver: self  
		   selector: @selector(finish:) 
			   name: NSFileHandleReadToEndOfFileCompletionNotification
			 object: nil];	
	
	NSString *path = @"/Path/To/Large/File"
	if (path == nil)
	{
		[[self text] setStringValue: @"Error, no path"];
		return;
	}
	
	NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath: path];
	
	[self setHandle: fh];
	
	[fh readToEndOfFileInBackgroundAndNotify];
}

- (void) finish: (NSNotification *)note
{
	NSLog(@"finish ");
	
	NSData *currentBuffer = [[note userInfo] objectForKey:NSFileHandleNotificationDataItem];
	
	[self setData: currentBuffer];
	
	[[self handle] closeFile];
	[self setHandle: nil];
	
	NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
	[nc removeObserver: self  
				  name: NSFileHandleReadToEndOfFileCompletionNotification 
				object: nil];
	
	NSLog(@"%d Bytes loaded!" , [currentBuffer length]);
}

@end

So etwas wie z.B. [NSRunLoop runObject: object withSelector: @selector(enter)]; um nun beliebigen Code auf der RunLoop zu managen gibt es allerdings nicht. Man ist auf die Cocoa bzw. Foundation Klassen beschränkt die das Framework liefert. Diese decken aber schon ein sehr großes Spektrum von Anwendungsfällen ab. Es können aber auch eigene “RunLoop Sources“ definiert werden, was allerdings hier nicht besprochen werden soll. Damit sollte der Zusammenhang, zwischen Callstack, blockierender Anwendung und RunLoop klar sein. Aber auf dem Callstack wartet noch jemand auf die Rückkehr des Programms, der AutoreleasePool...

« vorige Seitenächste Seite »