2010-03-09 22 views
29

Tôi đang cố gắng để xây dựng một FSM để kiểm soát một bộ đếm thời gian trong (iphone sdk) mục tiêu c. Tôi cảm thấy đó là một bước cần thiết, bởi vì tôi đã kết thúc bằng mã spaghetti khó chịu chứa các trang của câu lệnh if-then. Sự phức tạp, không dễ đọc và khó khăn trong việc thêm/thay đổi các tính năng dẫn tôi đến thử một giải pháp chính thức hơn như thế này.Làm thế nào để làm cho một máy nhà nước hữu hạn cơ bản trong mục tiêu-C

Trong ngữ cảnh của ứng dụng, trạng thái của bộ đếm thời gian xác định một số tương tác phức tạp với NSManagedObjects, Dữ liệu cốt lõi, v.v. Tôi đã bỏ tất cả chức năng đó ngay bây giờ, trong nỗ lực để có được một cái nhìn rõ ràng về mã FSM.

Vấn đề là, tôi không thể tìm thấy bất kỳ ví dụ nào về loại mã này trong Obj-C và tôi không tự tin về cách tôi đã dịch nó từ mã ví dụ C++ mà tôi đang sử dụng. (Tôi không biết C++ ở tất cả, vì vậy có một số đoán liên quan.) Tôi đang dựa trên phiên bản này của một thiết kế mô hình nhà nước về bài viết này: http://www.ai-junkie.com/architecture/state_driven/tut_state1.html. Tôi không làm một trò chơi, nhưng bài viết này phác thảo các khái niệm làm việc cho những gì tôi đang làm.

Để tạo mã (được đăng bên dưới), tôi phải học rất nhiều khái niệm mới, bao gồm giao thức obj-c, v.v. Bởi vì đây là những điều mới mẻ đối với tôi, cũng giống như mô hình thiết kế của nhà nước, tôi hy vọng một số ý kiến ​​phản hồi về việc thực hiện này. Đây có phải là cách bạn làm việc với các đối tượng giao thức hiệu quả trong obj-c không?

Đây là giao thức:

@class Timer; 
@protocol TimerState 

-(void) enterTimerState:(Timer*)timer; 
-(void) executeTimerState:(Timer*)timer; 
-(void) exitTimerState:(Timer*)timer; 

@end 

Đây là đối tượng Timer (trong rút gọn nhất hình thức của nó) tập tin tiêu đề:

@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; 

Và việc thực hiện hẹn giờ Object:

#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 

Đừng lo lắng rằng có những điều còn thiếu trong lớp học này. Nó không làm bất cứ điều gì thú vị được nêu ra. Tôi hiện chỉ đang đấu tranh với cú pháp chính xác. Hiện tại nó biên dịch (và hoạt động) nhưng phương thức isKindOfClass gọi các cảnh báo trình biên dịch gây ra (phương thức không được tìm thấy trong giao thức). Tôi không thực sự chắc chắn rằng tôi muốn sử dụng isKindOfClass anyway. Tôi đã nghĩ đến việc cho mỗi đối tượng id<TimerState> một chuỗi tên và sử dụng chuỗi đó thay thế.

Lưu ý khác: tất cả các khai báo id<TimerState> ban đầu là các khai báo TimerState *. Nó có vẻ hợp lý để giữ chúng như là tài sản. Không chắc chắn nếu nó có ý nghĩa với id<TimerState> của.

Dưới đây là một ví dụ về một trong các lớp nhà nước:

#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 

Một lần nữa, cho đến nay nó không làm bất cứ điều gì ngoại trừ thông báo rằng những gì giai đoạn (hoặc tiểu bang) nó ở Nhưng đó không phải là. điểm.

Điều tôi hy vọng tìm hiểu ở đây là liệu kiến ​​trúc này có được viết chính xác bằng ngôn ngữ obj-c hay không. Một vấn đề cụ thể mà tôi gặp phải là việc tạo ra các đối tượng id trong hàm init của timer. Như bạn có thể thấy, tôi đã nhận xét các bản phát hành, vì chúng đã gây ra cảnh báo "phát hành không tìm thấy trong giao thức". Tôi đã không chắc chắn làm thế nào để xử lý đó.

Điều tôi không cần là nhận xét về mã này quá mức hoặc không có nghĩa hình thức vô nghĩa hoặc bất kỳ điều gì. Nó đáng để tôi học điều này ngay cả khi những ý tưởng đó là sự thật. Nếu nó giúp, hãy nghĩ về nó như một thiết kế lý thuyết cho một FSM trong obj-c.

Cảm ơn bạn trước vì bất kỳ nhận xét hữu ích nào.

(điều này không giúp quá nhiều: Finite State Machine in Objective-C)

Trả lời

8

Khi bạn sử dụng giao thức làm công cụ sửa đổi loại, bạn có thể cung cấp danh sách giao thức được phân cách bằng dấu phẩy. Vì vậy, tất cả các bạn cần phải làm gì để thoát khỏi cảnh báo trình biên dịch là thêm NSObject vào danh sách giao thức như vậy:

- (void)setupTimer:(id<TimerState,NSObject>) timerState { 

    // Create scheduled timers, etc... 
} 
+15

Bạn cũng có thể có một giao thức phù hợp với một giao thức khác để không cần phải đề cập đến cả hai lần - khai báo nó như '@protocol TimerState '. Điều này nói với trình biên dịch hơn tất cả các đối tượng TimerState cũng phải tuân theo giao thức NSObject. – Chuck

+0

Mà gần như chắc chắn những gì bạn muốn làm trong trường hợp này. Điểm tuyệt vời. – jlehr

+0

Điều đó chắc chắn làm cho trình biên dịch trở nên hạnh phúc. – mwt

1

Tôi khá mới mẻ tại Objective-C, nhưng tôi sẽ đề nghị bạn nên nhìn vào thực hiện ANSI C thẳng cho sản phẩm Máy Nhà nước.

Chỉ vì bạn đang sử dụng Cocoa không có nghĩa là bạn phải sử dụng thông điệp Mục tiêu-C tại đây.

Trong ANSI C, cài đặt máy trạng thái có thể rất đơn giản và dễ đọc.

Lần triển khai cuối cùng của tôi trong C của FSM được chỉ định #define STATE_x hoặc liệt kê các loại cho các tiểu bang và có bảng con trỏ để thực hiện chức năng thực thi từng trạng thái.

+1

Bài viết ai-junkie.com được đề cập ở trên thảo luận tại sao thiết kế tôi đang cố gắng ở trên vượt trội so với thiết kế bảng chức năng. Tôi không biết rằng nó thực sự * là * một cách tốt hơn để đi, nhưng ý tưởng của ông là đáng đọc. – mwt

+0

Tôi đoán tôi nên nói "được cho là tốt hơn." – mwt

15

Tôi đề xuất sử dụng State Machine Compiler, nó sẽ xuất ra mã Objective-C. Tôi đã thành công tốt trong Java và Python bằng cách sử dụng nó.

Bạn không nên viết mã máy nhà nước bằng tay, bạn nên sử dụng thứ gì đó để tạo mã cho bạn. SMC sẽ tạo ra mã sạch rõ ràng, sau đó bạn có thể xem xét nếu bạn muốn học hỏi từ nó, hoặc bạn chỉ có thể sử dụng nó và được thực hiện với nó.

+1

Ồ. Tôi không biết về điều này. –

+1

Điều đó thật thú vị, mặc dù nó không thực sự giải quyết câu hỏi của tôi. – mwt

+1

Đây là câu trả lời hay nhất mà tôi đã thấy trên SO chưa – Nektarios

3

tôi muốn khuyên bạn nên xem Statec nó có một dsl chút thoải mái để làm FSM và kết quả đầu ra đang ObjC . Nó giống như mogenerator cho các máy nhà nước.

7

Nếu bạn muốn thực hiện rất đơn giản, Objective-C của một máy nhà nước tôi vừa phát hành TransitionKit, cung cấp một API được thiết kế tốt để triển khai các máy trạng thái. Nó được kiểm tra kỹ lưỡng, được tài liệu hóa, rất dễ sử dụng và không yêu cầu bất kỳ công cụ tạo mã hoặc công cụ bên ngoài nào.

Các vấn đề liên quan