Ich versuche, ein FSM zu erstellen, um einen Timer in (iPhone SDK) Ziel zu steuern c. Ich fühlte, dass es ein notwendiger Schritt war, weil ich sonst mit fiesen Spaghetti-Code endete, der Seiten von Wenn-Dann-Aussagen enthielt. Die Komplexität, die Unlesbarkeit und die Schwierigkeit, Funktionen hinzuzufügen/zu ändern, führen mich dazu, eine formellere Lösung wie diese zu versuchen.So erstellen Sie einen grundlegenden endlichen Automaten in Objective-C
Im Kontext der Anwendung bestimmt der Status des Zeitgebers einige komplexe Interaktionen mit NSManagedObjects, Core Data und so weiter. Ich habe diese Funktionalität für den Moment weggelassen, um den FSM-Code klar zu sehen.
Das Problem ist, ich kann keine Beispiele für diese Art von Code in Obj-C finden, und ich bin nicht so zuversichtlich, wie ich es aus dem C++ - Beispielcode, den ich verwendet habe, übersetzt habe. (Ich kenne C++ überhaupt nicht, also gibt es einige Raten.) Ich stütze diese Version eines Zustandsmusterdesigns auf diesem Artikel: http://www.ai-junkie.com/architecture/state_driven/tut_state1.html. Ich mache kein Spiel, aber dieser Artikel beschreibt Konzepte, die für das, was ich tue, funktionieren.
Um den Code (siehe unten) zu erstellen, musste ich eine Menge neuer Konzepte lernen, einschließlich obj-c-Protokolle und so weiter. Weil diese für mich neu sind, ebenso wie das Design des Staates, hoffe ich auf ein Feedback zu dieser Implementierung. Arbeiten Sie so effektiv mit Protokollobjekten in obj-c? Hier
ist das Protokoll:
@class Timer;
@protocol TimerState
-(void) enterTimerState:(Timer*)timer;
-(void) executeTimerState:(Timer*)timer;
-(void) exitTimerState:(Timer*)timer;
@end
Hier ist das Timer-Objekt (in seiner abgespeckten Form) Header-Datei:
@interface Timer : NSObject
{
id<TimerState> currentTimerState;
NSTimer *secondTimer;
id <TimerViewDelegate> viewDelegate;
id<TimerState> setupState;
id<TimerState> runState;
id<TimerState> pauseState;
id<TimerState> resumeState;
id<TimerState> finishState;
}
@property (nonatomic, retain) id<TimerState> currentTimerState;
@property (nonatomic, retain) NSTimer *secondTimer;
@property (assign) id <TimerViewDelegate> viewDelegate;
@property (nonatomic, retain) id<TimerState> setupState;
@property (nonatomic, retain) id<TimerState> runState;
@property (nonatomic, retain) id<TimerState> pauseState;
@property (nonatomic, retain) id<TimerState> resumeState;
@property (nonatomic, retain) id<TimerState> finishState;
-(void)stopTimer;
-(void)changeState:(id<TimerState>) timerState;
-(void)executeState:(id<TimerState>) timerState;
-(void) setupTimer:(id<TimerState>) timerState;
Und die Timer Objekt Implementierung:
#import "Timer.h"
#import "TimerState.h"
#import "Setup_TS.h"
#import "Run_TS.h"
#import "Pause_TS.h"
#import "Resume_TS.h"
#import "Finish_TS.h"
@implementation Timer
@synthesize currentTimerState;
@synthesize viewDelegate;
@synthesize secondTimer;
@synthesize setupState, runState, pauseState, resumeState, finishState;
-(id)init
{
if (self = [super init])
{
id<TimerState> s = [[Setup_TS alloc] init];
self.setupState = s;
//[s release];
id<TimerState> r = [[Run_TS alloc] init];
self.runState = r;
//[r release];
id<TimerState> p = [[Pause_TS alloc] init];
self.pauseState = p;
//[p release];
id<TimerState> rs = [[Resume_TS alloc] init];
self.resumeState = rs;
//[rs release];
id<TimerState> f = [[Finish_TS alloc] init];
self.finishState = f;
//[f release];
}
return self;
}
-(void)changeState:(id<TimerState>) newState{
if (newState != nil)
{
[self.currentTimerState exitTimerState:self];
self.currentTimerState = newState;
[self.currentTimerState enterTimerState:self];
[self executeState:self.currentTimerState];
}
}
-(void)executeState:(id<TimerState>) timerState
{
[self.currentTimerState executeTimerState:self];
}
-(void) setupTimer:(id<TimerState>) timerState
{
if ([timerState isKindOfClass:[Run_TS class]])
{
secondTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(currentTime) userInfo:nil repeats:YES];
}
else if ([timerState isKindOfClass:[Resume_TS class]])
{
secondTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(currentTime) userInfo:nil repeats:YES];
}
}
-(void) stopTimer
{
[secondTimer invalidate];
}
-(void)currentTime
{
//This is just to see it working. Not formatted properly or anything.
NSString *text = [NSString stringWithFormat:@"%@", [NSDate date]];
if (self.viewDelegate != NULL && [self.viewDelegate respondsToSelector:@selector(updateLabel:)])
{
[self.viewDelegate updateLabel:text];
}
}
//TODO: releases here
- (void)dealloc
{
[super dealloc];
}
@end
Keine Sorge, dass in dieser Klasse Dinge fehlen. Es macht noch nichts interessantes. Ich bin gerade dabei, die Syntax zu korrigieren. Derzeit kompiliert es (und funktioniert), aber die Aufrufe der isKindOfClass-Methode verursachen Compiler-Warnungen (Methode wurde im Protokoll nicht gefunden). Ich bin mir nicht wirklich sicher, ob ich isKindOfClass trotzdem benutzen möchte. Ich dachte daran, jedem id<TimerState>
Objekt eine Namenszeichenfolge zu geben und diese stattdessen zu verwenden.
Auf eine andere Anmerkung: alle diese id<TimerState>
Deklarationen waren ursprünglich TimerState * Deklarationen. Es schien sinnvoll, sie als Eigenschaften zu behalten. Nicht sicher, ob es mit id<TimerState>
's Sinn macht. Hier
ist ein Beispiel für eine der Zustandsklassen:
#import "TimerState.h"
@interface Setup_TS : NSObject <TimerState>{
}
@end
#import "Setup_TS.h"
#import "Timer.h"
@implementation Setup_TS
-(void) enterTimerState:(Timer*)timer{
NSLog(@"SETUP: entering state");
}
-(void) executeTimerState:(Timer*)timer{
NSLog(@"SETUP: executing state");
}
-(void) exitTimerState:(Timer*)timer{
NSLog(@"SETUP: exiting state");
}
@end
wieder so weit es nichts tut, außer bekannt, dass in welcher Phase (oder Unter Zustand) ist es in Aber das ist nicht die. Punkt.
Was ich hier zu lernen hoffe ist, ob diese Architektur korrekt in der Obj-c-Sprache zusammengesetzt ist. Ein spezifisches Problem, auf das ich stoße, ist die Erzeugung der ID-Objekte in der Init-Funktion des Timers. Wie Sie sehen können, habe ich die Releases auskommentiert, weil sie eine Warnung "Freigabe nicht im Protokoll gefunden" verursachten. Ich war mir nicht sicher, wie ich damit umgehen sollte.
Was ich nicht brauche, sind Kommentare über diesen Code, der übertrieben oder bedeutungsloser Formalismus ist, oder was auch immer. Es lohnt sich, das zu lernen, auch wenn diese Ideen wahr sind. Wenn es hilft, denke an es als theoretisches Design für eine FSM in obj-c.
Vielen Dank im Voraus für hilfreiche Kommentare.
(auch dies nicht hilft viel: Finite State Machine in Objective-C)
Sie können auch ein Protokoll haben, das einem anderen Protokoll entspricht, so dass es nicht notwendig ist, jedes Mal zu erwähnen - erklären Sie es wie '@protocol TimerState'. Dies teilt dem Compiler mit, dass alle TimerState-Objekte auch dem NSObject-Protokoll entsprechen müssen. –
Chuck
Welches wäre fast sicher, was Sie in diesem Fall tun möchten. Ausgezeichneter Punkt. – jlehr
Das macht den Compiler sicherlich glücklich. – mwt