I created this state machine to handle events. I'm unsure about my implementation firstly because I don't use a transition table or anything like that, instead, I simply input the next event/state that needs to happen. I think this is cleaner than calling method to handle event b inside the method to handle event a, right?
Secondarily, since some of the methods have parameters - and this is not swift where enum members can have associated values, I added a param for that via withObj argument. I don't see a problem with this but want to know if this is confusing/violating something from your perspective.
I also found this useful for async task, so after the task completes I set resulting value to a local variable then update state to packData using that stored value amongst many other values already existing. This value is packed in a byte array which cannot be used in blocks so this was a useful workaround - although I haven't tested yet.
States are defined via enum like so:
typedef NS_ENUM(NSUInteger, ExampleState) {
ExampleStateIdle,
ExampleStateWaitingForAsyncTask,
ExampleStateReadyToPack,
ExampleStateRespond,
ExampleStateSomethingInProgress,
ExampleStateSomething2InProgress,
ExampleStateComplete
};
Here I define a method to handle each event using helper methods. I also have an extra argument for any state that might have an associated value.
- (void)updateState:(ExampleState)state withObj:(id)obj {
self.currentState = state;
switch (state) {
case ExampleStateWaitingForAsyncTask:
[self getAsyncInfo];
break;
case ExampleStateReadyToPack:
[self packData];
break;
case ExampleStateRespond:
[self respond:obj];
break;
case ExampleStateComplete:
[self showPopup];
break;
default:
break;
}
}
Example usage:
-(void)packData {
NSData *data = [NSData dataWithBytes:&result length:resultIndex];
// next step is to respond to `client`/`central` with data
[self updateState:ExampleStateRespond withObj:data];
}
Related
Ok, I have a signal that sends an event when a certain method of a protocol gets called in response to some data getting retrieved from the server:
self.dataReceivedSignal = [[self rac_signalForSelector:#selector(didReceiveData:) fromProtocol:#protocol(DataServiceDelegate)] mapReplace:#YES];
This signal is then used to fire another signal that formats and returns the data:
- (RACSignal *)dataSignal
{
return [RACSignal combineLatest:#[self.dataReceivedSignal] reduce:^id(NSNumber * received){
...
return my_data;
}];
}
This view controller just listens to this second signal to get the data.
This works fine.
The problem is, the second time I enter this view controller, I don't want to load the data again, so I save it locally and do this:
if (!self.alreadyHasData) {
self.dataService = [[DataService alloc] init];
self.dataService.delegate = self;
[self.dataService getData];
} else {
self.dataReceivedSignal = [RACSignal return:#YES];
}
In case I already have the data, I'm replacing the dataReceivedSignal with a new one that just sends #YES and completes.
This works too, but that if/else doesn't seem too functional to me. Is this the correct approach?
Thanks.
First of all you can exchange combineLatest to map.
If you want not reload data if it's already loaded, you can write something like this:
- (RACSignal *)dataSignal
{
if (!_dataSignal) {
RACMulticastConnection *dataConnection = [[self.dataReceivedSignal map:^id(NSNumber * received){
/// ...
return my_data;
}] multicast:[RACReplaySubject replaySubjectWithCapacity:1]];
// Only do all of the above after one subscriber has attached.
_dataSignal = [RACSignal defer:^{
[dataConnection connect];
return dataConnection.signal;
}];
}
return _dataSignal;
}
And don't matter how much subscribers signal will have, retrieve data block will be called only one time.
Simpler code = better code. I think you can solve task with simpler solution without RAC.
I am a novice programmer, and I've just started reading about decision tables. I have read Chapter 18 in Code Complete and it was very enlightening. I looked around the web to try to find any kind of example of decision tables in Objective-C and I was unable to find any boilerplate or real world examples of how to implement this.
I am programming a game in Objective-C in my spare time, and I have been dealing with increasing complexity for the rules of the game. There are a handful of somewhat deeply nested if-else statements, as well as a few switch statements that already have 10 or more cases to deal with. I think it would be easier to work with decision tables, but I have no idea how to implement this in Objective-C for something non-trivial like the logic of a game.
For example, I need different methods to execute for different combinations of states. How would I implement a decision table in Objective-C that could take different combinations of states as keys, and run specific logic based on the combination of them?
Well I thought about decision tables in Objective-C some more and came up with a solution to implement a basic one. I will not post the entire code here, just the snippets that make the decision table work and their basic purpose. I posted this over at Code Review SE if you want to see the full code and some great suggestions for how to improve it. I'm posting this now because someone posted a comment requesting that I do so, but I will definitely end up improving this and integrating the suggestions from the review. Anyway, here is the code.
First before the initialization method I establish a number of NSString constants that will be used as the keys in an NSDictionary.
//Two options for the decision table, either access the dictionary directly with 0-x, the enum values, or make strings for their names
//the advantage of strings is that it is more extensible, and the position in the enum doesnt matter
NSString* const kEnemyMovementStateJustSpawned = #"enemyMovementStateJustSpawned";
NSString* const kEnemyMovementStateIdle = #"enemyMovementStateIdle";
NSString* const kEnemyMovementStateNeedsMoving = #"enemyMovementStateNeedsMoving";
NSString* const kEnemyMovementStateToFloor = #"enemyMovementStateToFloor";
NSString *const kEnemyMovementStateAtDestinationFloor = #"enemyMovementStateAtDestinationFloor";
NSString* const kEnemyMovementStateToFloorExit = #"enemyMovementStateToFloorExit";
NSString* const kEnemyMovementStateToAttackWalls = #"enemyMovementStateToAttackWalls";
NSString* const kEnemyMovementStateToAttackFloor = #"enemyMovementStateToAttackFloor";
NSString* const kEnemyMovementStateToAttackRoom = #"enemyMovementStateToAttackRoom";
Then I use these constants along with the names of methods in the class to build the NSDictionary:
-(void) setupDecisionTable {
//the string objects are the names of methods in the class
_decisionTable = #{kEnemyMovementStateJustSpawned: #"doEnemyJustSpawned",
kEnemyMovementStateIdle: #"doEnemyIdle",
kEnemyMovementStateNeedsMoving: #"doEnemyNeedsMoving",
kEnemyMovementStateToFloorExit: #"doFloorMovement",
kEnemyMovementStateToFloor: #"doVerticalMovement",
kEnemyMovementStateAtDestinationFloor: #"doEnemyAtDestinationFloor",
kEnemyMovementStateToAttackWalls: #"doFloorMovement",
kEnemyMovementStateToAttackFloor: #"doFloorMovement",
kEnemyMovementStateToAttackRoom: #"doFloorMovement"
};
}
Then every tick I call this method, which executes the method with the name of the object pulled from the dictionary:
-(void) doMovement {
//the selector is formed from a string inside the decision table dictionary
SEL methodToCallName = NSSelectorFromString([_decisionTable objectForKey:[self stringForState:self.state]]);
if (methodToCallName) {
IMP functionPointer = [self methodForSelector:methodToCallName];
void (*methodToCall)(id, SEL) = (void *)functionPointer;
methodToCall(self, methodToCallName);
}
}
-(NSString *) stringForState:(EnemyMovementState)state {
switch (state) {
case EnemyMovementStateJustSpawned:
return kEnemyMovementStateJustSpawned;
case EnemyMovementStateIdle:
return kEnemyMovementStateIdle;
case EnemyMovementStateNeedsMoving:
return kEnemyMovementStateNeedsMoving;
case EnemyMovementStateToFloor:
return kEnemyMovementStateToFloor;
case EnemyMovementStateAtDestinationFloor:
return kEnemyMovementStateAtDestinationFloor;
case EnemyMovementStateToFloorExit:
return kEnemyMovementStateToFloorExit;
case EnemyMovementStateToAttackWalls:
return kEnemyMovementStateToAttackWalls;
case EnemyMovementStateToAttackFloor:
return kEnemyMovementStateToAttackFloor;
case EnemyMovementStateToAttackRoom:
return kEnemyMovementStateToAttackRoom;
default:
return nil;
}
}
Finally here are a couple of the methods that execute, just for a complete example:
-(void) doEnemyIdle {
if ([self checkFloorsForJobs]) {
self.state = EnemyMovementStateNeedsMoving;
} else {
[self doIdleMovement];
}
}
-(void) doEnemyNeedsMoving {
[self calculateFloorExitPositionByFloor];
self.state = EnemyMovementStateToFloorExit;
}
This is a pretty simple implementation. Currently it can only deal with one input, and a better decision table would be able to evaluate multiple inputs and provide the proper output. I think it could be extended by having an intermediate method that took the state combined with other variables to choose the proper object from the dictionary.
After doing all this, I'm not sure that decision tables are worth the effort in Objective-C. I do not know if the code is easier to understand than a switch statement. In order to add new logic to the code, it has to be modified in more places than a switch statement would seem to require. I provide this code as an example, and it would be cool to see other versions of decision tables in Objective-C if anyone has one.
The client I'm building is using Reactive Cocoa with Octokit and so far it has been going very well. However now I'm at a point where I want to fetch a collection of repositories and am having trouble wrapping my head around doing this the "RAC way"
// fire this when an authenticated client is set
[[RACAbleWithStart([GHDataStore sharedStore], client)
filter:^BOOL (OCTClient *client) {
return client != nil && client.authenticated;
}]
subscribeNext:^(OCTClient *client) {
[[[client fetchUserRepositories] deliverOn:RACScheduler.mainThreadScheduler]
subscribeNext:^(OCTRepository *fetchedRepo) {
NSLog(#" Received new repo: %#",fetchedRepo.name);
}
error:^(NSError *error) {
NSLog(#"Error fetching repos: %#",error.localizedDescription);
}];
} completed:^{
NSLog(#"Completed fetching repos");
}];
I originally assumed that -subscribeNext: would pass an NSArray, but now understand that it sends the message every "next" object returned, which in this case is an OCTRepository.
Now I could do something like this:
NSMutableArray *repos = [NSMutableArray array];
// most of that code above
subscribeNext:^(OCTRepository *fetchedRepo) {
[repos addObject:fetchedRepo];
}
// the rest of the code above
Sure, this works, but it doesn't seem to follow the functional principles that RAC enables. I'm really trying to stick to conventions here. Any light on capabilities of RAC/Octokit are greatly appreciated!
It largely depends on what you want to do with the repositories afterward. It seems like you want to do something once you have all the repositories, so I'll set up an example that does that.
// Watch for the client to change
RAC(self.repositories) = [[[[[RACAbleWithStart([GHDataStore sharedStore], client)
// Ignore clients that aren't authenticated
filter:^ BOOL (OCTClient *client) {
return client != nil && client.authenticated;
}]
// For each client, execute the block. Returns a signal that sends a signal
// to fetch the user repositories whenever a new client comes in. A signal of
// of signals is often used to do some work in response to some other work.
// Often times, you'd want to use `-flattenMap:`, but we're using `-map:` with
// `-switchToLatest` so the resultant signal will only send repositories for
// the most recent client.
map:^(OCTClient *client) {
// -collect will send a single value--an NSArray with all of the values
// that were send on the original signal.
return [[client fetchUserRepositories] collect];
}]
// Switch to the latest signal that was returned from the map block.
switchToLatest]
// Execute a block when an error occurs, but don't alter the values sent on
// the original signal.
doError:^(NSError *error) {
NSLog(#"Error fetching repos: %#",error.localizedDescription);
}]
deliverOn:RACScheduler.mainThreadScheduler];
Now self.repositories will change (and fire a KVO notification) whenever the repositories are updated from the client.
A couple things to note about this:
It's best to avoid subscribeNext: whenever possible. Using it steps outside of the functional paradigm (as do doNext: and doError:, but they're also helpful tools at times). In general, you want to think about how you can transform the signal into something that does what you want.
If you want to chain one or more pieces of work together, you often want to use flattenMap:. More generally, you want to start thinking about signals of signals--signals that send other signals that represent the other work.
You often want to wait as long as possible to move work back to the main thread.
When thinking through a problem, it's sometimes valuable to start by writing out each individual signal to think about a) what you have, b) what you want, and c) how to get from one to the other.
EDIT: Updated to address #JustinSpahrSummers' comment below.
There is a -collect operator that should do exactly what you're looking for.
// Collect all receiver's `next`s into a NSArray. nil values will be converted
// to NSNull.
//
// This corresponds to the `ToArray` method in Rx.
//
// Returns a signal which sends a single NSArray when the receiver completes
// successfully.
- (RACSignal *)collect;
I have a method on ParentViewModel which returns an RACSequence of ViewModel objects like so:
- (RACSequence *) viewModels
{
return [self.models.rac_sequence map:^id(Model *model) {
return [[ViewModel alloc] initWithModel: model];
}];
}
Each of the ViewModels has a state property on which is an enum and has 3 states: NotStarted, InProgress and Completed. When all the ViewModels in my sequence have the state Completed I know ParentViewModel is valid. I have a validSignal on the ParentViewModel which I want to derive the fact that is valid from the viewModels sequence. At the moment I have this code:
BOOL valid = [[self viewModels] all:^BOOL(ViewModel *vm) {
return vm.state == Completed;
}];
Which gives me an indicator if all ViewModels in the sequence are valid. How can I then turn this into a RACSignal which will update every time the state property on one of the ViewModels changes?
You need first to turn state into a RACSignal, and then everything is easy from that point.
The final code will be something like the following:
RACSignal *valid = [[RACSignal combineLatest:
[[self viewModels] map:^id(ViewModel *viewModel) {
return RACAbleWithStart(viewModel, state);
}]
]
map:^(RACTuple *states) {
return #([states.rac_sequence all:^BOOL(NSNumber *state) {
return state.unsignedIntegerValue == Completed;
}]);
}
];
The first block maps each of your view models into a signal that observes the state property (with the starting value as first value of the signal).
combineLatest: will take a collection of RACSignals and will create a new signal that fires everytime one of the underlaying signals changes, and sends a RACTuple with the value of each signal.
That RACTuple is then converted into a RACSequence, and we can generate a value of #YES or #NO depending if all the values are Completed or not.
I think the result is the signal you were looking for.
(Disclaimer: I’m new to ReactiveCocoa, so there may be an easier way).
I am currently writing a program to help me control complex lights installations. The idea is I tell the program to start a preset, then the app has three options (depending on the preset type)
1) the lights go to one position (so only one group of data sent when the preset starts)
2) the lights follows a mathematical equation (ex: sinus with a timer to make smooth circles)
3) the lights respond to a flow of data (ex midi controller)
So I decided to go with an object I call the AppBrain, that receive data from the controllers and the templates, but also is able to send processed data to the lights.
Now, I come from non-native programming, and I kinda have trust issues concerning working with a lot of processing, events and timing; as well as troubles with understanding 100% the Cocoa logic.
This is where the actual question starts, sorry
What I want to do, would be when I load the preset, I parse it to prepare the timer/data receive event so it doesn't have to go trough every option for 100 lights 100 times per second.
To explain more deeply, here's how I would do it in Javascript (crappy pseudo code, of course)
var lightsFunctions = {};
function prepareTemplate(theTemplate){
//Let's assume here the template is just an array, and I won't show all the processing
switch(theTemplate.typeOfTemplate){
case "simpledata":
sendAllDataTooLights(); // Simple here
break;
case "periodic":
for(light in theTemplate.lights){
switch(light.typeOfEquation){
case "sin":
lightsFunctions[light.id] = doTheSinus; // doTheSinus being an existing function
break;
case "cos":
...
}
}
function onFrame(){
for(light in lightsFunctions){
lightsFunctions[light]();
}
}
var theTimer = setTimeout(onFrame, theTemplate.delay);
break;
case "controller":
//do the same pre-processing without the timer, to know which function to execute for which light
break;
}
}
}
So, my idea is to store the processing function I need in an NSArray, so I don't need to test on each frame the type and loose some time/CPU.
I don't know if I'm clear, or if my idea is possible/the good way to go. I'm mostly looking for algorithm ideas, and if you have some code that might direct me in the good direction... (I know of PerformSelector, but I don't know if it is the best for this situation.
Thanks;
I_
First of all, don't spend time optimizing what you don't know is a performance problem. 100 iterations of the type is nothing in the native world, even on the weaker mobile CPUs.
Now, to your problem. I take it you are writing some kind of configuration / DSL to specify the light control sequences. One way of doing it is to store blocks in your NSArray. A block is the equivalent of a function object in JavaScript. So for example:
typedef void (^LightFunction)(void);
- (NSArray*) parseProgram ... {
NSMutableArray* result = [NSMutableArray array];
if(...) {
LightFunction simpledata = ^{ sendDataToLights(); };
[result addObject:simpleData];
} else if(...) {
Light* light = [self getSomeLight:...];
LightFunction periodic = ^{
// Note how you can access the local scope of the outside function.
// Make sure you use automatic reference counting for this.
[light doSomethingWithParam:someParam];
};
[result addObject:periodic];
}
return result;
}
...
NSArray* program = [self parseProgram:...];
// To run your program
for(LightFunction func in program) {
func();
}