• Das kreative Thema des Monats Oktober wird vor allem diejenigen unter Euch besonders ansprechen, denen Do-it-yourself quasi im Blut liegt. Zeigt uns Eure Improvisationskünste oder fotografiert, was andere Leute sich so im Alltag zusammengestrickt haben. --> Zum Mitmachen bitte hier klicken

[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
 
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
 
Ä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
 
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...
 
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
 
Siehe NSRunLoop.

Ich habe es Dir schnell gebaut.
Siehe Anhang.

Viele Grüße
 

Anhänge

  • Like
Reaktionen: Josa
Ach ja, freigeben musst Du die Objekte mit dealloc selbst ;-)

Bitte lies noch etwas zu Setter und Getter.

Viele Grüße
 
Zum Beispiel das man mit Getter und Setter nicht unsinnig um sich werfen soll ;)
 
Man kann es auch ordentlich schreiben ;-)
Aber sonst wird ihm langweilig.

Viele Grüße
 
Danke für die Antworten! Das funktioniert einwandfrei so.
 
@Josa, schau dir auch mal das Memory Management System von Objective-C an... denn in deinem Code machst du einige Fehler... :)

Gruss ppocket
 
@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.