StephenCollins
StephenCollins

Reputation: 805

Saving data from block into an NSMutableDictionary, and blocks in general

I am having a lot of trouble wrapping my head around the best way to use blocks. I am trying to retrieve pedometer data, and the method of accessing the data is a block...

[self.pedometer queryPedometerDataFromDate:yesterday
                                        toDate:midnightOfToday
                                   withHandler:^(CMPedometerData *pedometerData, NSError *error) {

         dispatch_async(dispatch_get_main_queue(), ^{
             if (error) {
                 NSLog(@"Pedometer is NOT available.");
             }
             else {
                 NSLog(@"Steps %@", pedometerData.numberOfSteps);
                 yesterdaysNumbersLabel.text = [pedometerData.numberOfSteps stringValue];
                 [pedometerDictionary setValue:[pedometerData.numberOfSteps stringValue] forKey:@"2"];
             }
         });
     }];

Using the above code I am able to get the data, log the data, and update the label on the screen, But I can't figure out how to set the data into an array or dictionary so I can do something else with it.

I understand why the arrays and dictionaries are always null... the blocks are running on a different thread and I am accessing them before the blocks have completed.

Can someone help me get through my head how to do something more with the data.

Update 1:

Right now I have this in .h

@property (strong, atomic) NSMutableDictionary *pedometerDictionary;

and I am synthesizing it in .m and I call this...

[self getNumbersForYesterday];
NSLog(@"Dictionary: %@", pedometerDictionary);

...which runs the above function and immediately tries to log the result. And like I said, I understand all the reasons it is NOT working. I just need to figure out how to change what i am doing to get it working.

Update 2:

This is in .h

@property (strong, atomic) NSMutableDictionary *pedometerDictionary;

and this is in .m

@synthesize pedometerDictionary;

- (id)init {
    self = [super init];
    if (self != nil) {
        self.pedometerDictionary = [[NSMutableDictionary alloc] init];
    }
    return self;
}

and I am using it like this.

[self getNumbersForYesterday];
NSLog(@"Dictionary: %@", self.pedometerDictionary);

to call this.

- (void)getNumbersForYesterday {

    [self.pedometer queryPedometerDataFromDate:yesterday
                                        toDate:midnightOfToday
                                   withHandler:^(CMPedometerData *pedometerData, NSError *error) {

         dispatch_async(dispatch_get_main_queue(), ^{
             if (error) {
                 NSLog(@"Pedometer is NOT available.");
             }
             else {
                 NSLog(@"Steps %@", pedometerData.numberOfSteps);
                 yesterdaysNumbersLabel.text = [pedometerData.numberOfSteps stringValue];
                 [self.pedometerDictionary setValue:[pedometerData.numberOfSteps stringValue] forKey:@"2"];

             }

         });
     }];

}

If I just wanted to keep all the work in the block I would be fine. What I have come to understand is that since blocks are asynchronous, I am trying to NSLog my dictionary, and the block isn't finished running yet. So, my dictionary is still NULL.

Upvotes: 1

Views: 475

Answers (2)

Conor
Conor

Reputation: 1777

The issue you are having is not a block timing issue, your dictionary should never be nil at worst it would contain no values.

You need to create your dictionary before using it. The appropriate place would be init method for most objects. If you are creating your object in Interface Builder then the method should be awakeFromNib.

To do something with the dictionary you can use an NSTimer or call a method from queryPedometerDataFromDate block handler. The use of @synchronized() directive is an example of how to keep access to the dictionary from overlapping at the same time in a threaded environment. This is not the case in this particular example as you are dispatching on the main thread and NSTimer also runs on the main thread. But should you go threaded @synchronized() would keep you from overlapping access.

@interface HelloWorld : NSObject
@property (retain, atomic) NSMutableDictionary *pedometerDictionary;
@property (retain, nonatomic) NSTimer *timer;
@end


@implementation HelloWorld
@synthesize pedometerDictionary, timer;

...

- (id)init {
    self = [super init];
    if (self != nil) {
        self.pedometerDictionary = [NSMutableDictionary dictionary];
        self.timer = [NSTimer timerWithTimeInterval:5.0 target:self selector:@selector(doSomethingInterestingWithDictionary:) userInfo:nil repeats:YES];
    }
    return self;
}

or

- (void)awakeFromNib {
    self.pedometerDictionary = [NSMutableDictionary dictionary];
    self.timer = [NSTimer timerWithTimeInterval:5.0 target:self selector:@selector(doSomethingInterestingWithDictionary:) userInfo:nil repeats:YES];
}

...

- (void)getNumbersForYesterday {
[self.pedometer queryPedometerDataFromDate:yesterday
                                    toDate:midnightOfToday
                               withHandler:^(CMPedometerData *pedometerData, NSError *error) {

     dispatch_async(dispatch_get_main_queue(), ^{
         if (error) {
             NSLog(@"Pedometer is NOT available.");
         }
         else {
             NSLog(@"Steps %@", pedometerData.numberOfSteps);
             yesterdaysNumbersLabel.text = [pedometerData.numberOfSteps stringValue];
             @synchronized (self) {
                 [self.pedometerDictionary setValue:[pedometerData.numberOfSteps stringValue] forKey:@"2"];
             }
             [self doSomethingInterestingWithDictionary:nil];
         }
     });
 }];
}


// Will be called when queryPedometerDataFromDate returns and from a timer every 5 seconds.
- (void)doSomethingInterestingWithDictionary:(NSTimer *)aTimer {
    @synchronized (self) {
        NSLog(@"My days dictionary: %@", self.pedometerDictionary);
    }
}

Upvotes: 0

bbum
bbum

Reputation: 162722

Dollars to donuts, your pedometerDictionary was never created in the first place (or it was, but the declaration isn't in a useful spot).

I.e. where is your line of code that says pedometerDictionary = [[NSMutableDictionary alloc] init];? And where is pedometerDictionary declared? How did you try to NSLog() values from it?

Also, use setObject:forKey:.


It is also odd that it is named pedometerDictionary. That is evidence that it is either declared as a global (which it shouldn't be), a local variable of whatever method contains the above code (which won't work), or you are declaring and using an instance variable directly.

Upvotes: 1

Related Questions