[Cocoa] Binding und Thread

Josa

Tydemans Early Worcester
Registriert
31.01.07
Beiträge
388
Ich hoffe mir kann jemand bei meinem Probel helfen.

Ich möchte das in einen Status Menu eine Wert regelmäßig erneuert wird. Ich habe dafür einen Worker-Thread der einen String in einer Schleife ändert. ein Menu Item ist an den String gebunden (Binding: Controller.foo).

Das funktioniert soweit auch. Das Problem ist nur, dass ich das Programm aufhängt wenn das Menu geöffnet ist und der String (foo) geändert wird.

Wie kann ich das verhindern?

Controller.h
Code:
#import <Cocoa/Cocoa.h>

@interface Controller : NSObject {
    
    IBOutlet NSMenu *mainMenu;
    NSStatusItem *mainItem;
    NSString* foo;

}

- (NSString*) foo;
- (void) setFoo: (NSString*)x;
+ (void) count: (id)param;

@end
Controller.m
Code:
#import "Controller.h"


@implementation Controller

- (void) awakeFromNib {
    
    mainItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:-1] retain];
    [mainItem setTitle:@"test"];
    [mainItem setMenu:mainMenu];
    [mainItem setHighlightMode:YES];
    
    [NSThread detachNewThreadSelector:@selector(count:) toTarget:[Controller class] withObject:self];
}

- (NSString *) foo {
    NSLog(@"Get foo: %@", foo);
    return foo;
}

- (void) setFoo: (NSString *)x {
    NSLog(@"Set foo: %@", x);
    foo = x;
}

+(void)count:(id)param {
    NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
    int i = 0;
    while (i < 100) {
        [(Controller*)param setFoo: [NSString stringWithFormat:@"foo: %d", i++] ];
        sleep(1);
    }
    [autoreleasepool release];
}

@end
Console:
Code:
[Session started at 2009-07-10 13:02:15 +0200.]
2009-07-10 13:02:16.176 MenuTest[7700:10b] Get foo: (null)
2009-07-10 13:02:16.198 MenuTest[7700:390b] Set foo: foo: 0
2009-07-10 13:02:16.201 MenuTest[7700:390b] Get foo: foo: 0
2009-07-10 13:02:17.202 MenuTest[7700:390b] Set foo: foo: 1
2009-07-10 13:02:17.213 MenuTest[7700:390b] Get foo: foo: 1
2009-07-10 13:02:18.214 MenuTest[7700:390b] Set foo: foo: 2
2009-07-10 13:02:18.214 MenuTest[7700:390b] Get foo: foo: 2

[Session started at 2009-07-10 13:02:18 +0200.]
Loading program into debugger…
GNU gdb 6.3.50-20050815 (Apple version gdb-962) (Sat Jul 26 08:14:40 UTC 2008)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-apple-darwin".Program loaded.
sharedlibrary apply-load-rules all
Attaching to program: `/Users/josa/Desktop/MenuTest/build/Debug/MenuTest.app/Contents/MacOS/MenuTest', process 7700.
[Switching to process 7700 thread 0x390b]
kill
 

LittlePixel

Strauwalds neue Goldparmäne
Registriert
09.07.08
Beiträge
641
Ich habe Deinen Code nur überflogen.
Bist Du neu in der Materie? (siehe Setter + Getter)

Warum einen neuen Thread anlegen?
Erzeuge einen Timer, der alle paar Sekunden losgeht.

Viele Grüße
 

MacApple

Schöner von Bath
Registriert
05.01.04
Beiträge
3.652
Änderungen am GUI darf man auch nur vom Main-Thread aus machen. Daher gibt es auch eine Methode performSelectorOnMainThread:withObject:waitUntilDone:. Aber der Timer ist hier einfach die bessere Lösung.

MacApple
 

Josa

Tydemans Early Worcester
Registriert
31.01.07
Beiträge
388
Danke für die Antworten. Ich werde mir den Timer mal anschauen.

@LittlePixel Wirklich Erfahrung habe ich mit Objective-C und Cocoa wirklich noch nicht...
 

Josa

Tydemans Early Worcester
Registriert
31.01.07
Beiträge
388
Das funktioniert so schon mal. Nur wird jetzt der Menu-Item-Title nicht mehr aktualisiert wenn das Menu geöffnent ist. Wie könnte ich das lösen?

Controller.m
Code:
#import "Controller.h"


@implementation Controller

- (void) awakeFromNib {
    
    mainItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:-1] retain];
	[mainItem setTitle:@"test"];
    [mainItem setMenu:mainMenu];
    [mainItem setHighlightMode:YES];
	
	timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(count:) userInfo:NULL repeats:YES];
}

- (IBAction) quite: (id) sender {
	[NSApp terminate: sender];
}

- (NSString *) foo {
	return foo;
}

- (void) setFoo: (NSString *)x {
	foo = x;
}

- (void) count: (NSTimer*) countTimer {
	[self setFoo: [NSString stringWithFormat:@"foo: %d", i++] ];
}

@end
 

LittlePixel

Strauwalds neue Goldparmäne
Registriert
09.07.08
Beiträge
641
Siehe NSRunLoop.

Ich habe es Dir schnell gebaut.
Siehe Anhang.

Viele Grüße
 

Anhänge

  • Test.zip
    53,9 KB · Aufrufe: 82
  • Like
Reaktionen: Josa

LittlePixel

Strauwalds neue Goldparmäne
Registriert
09.07.08
Beiträge
641
Ach ja, freigeben musst Du die Objekte mit dealloc selbst ;)

Bitte lies noch etwas zu Setter und Getter.

Viele Grüße
 

Irgendein Held

Oberösterreichischer Brünerling
Registriert
17.06.07
Beiträge
714
Zum Beispiel das man mit Getter und Setter nicht unsinnig um sich werfen soll ;)
 

LittlePixel

Strauwalds neue Goldparmäne
Registriert
09.07.08
Beiträge
641
Man kann es auch ordentlich schreiben ;)
Aber sonst wird ihm langweilig.

Viele Grüße
 

Josa

Tydemans Early Worcester
Registriert
31.01.07
Beiträge
388
Danke für die Antworten! Das funktioniert einwandfrei so.
 

Poljpocket

Salvatico di Campascio
Registriert
07.01.07
Beiträge
432
@Josa, schau dir auch mal das Memory Management System von Objective-C an... denn in deinem Code machst du einige Fehler... :)

Gruss ppocket
 

Josa

Tydemans Early Worcester
Registriert
31.01.07
Beiträge
388
@Josa, schau dir auch mal das Memory Management System von Objective-C an... denn in deinem Code machst du einige Fehler... :)

Gruss ppocket

Danke für den Tipp, dass werde ich machen.

Momentan ging es mir aber so wie so erstmal nur um das beschriebene Problem.