How do I keep a parameter passed into a block - ios

I'm using the iOS motion manager and it works by using a block Handler. I have an "Acceleration" object with ...
#property (atomic, readonly) CMAccelerometerData * rawAccelerometerData;
In the .m file, I have...
-(void) startServices{
[_motionManager startAccelerometerUpdatesToQueue:_queue withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
_rawAccelerometerData = accelerometerData;
dispatch_async(dispatch_get_main_queue(),^(void){
[delegate accelerometerUpdate:accelerometer];
});
}];
}
The Delegate is a view controller:
-(void) accelerometerUpdate:(Accelerometer *) accelerometer {
_accelX.text = [NSString stringWithFormat:#"%f", accelerometer.rawAccelerometerData.acceleration.x];
_accelY.text = [NSString stringWithFormat:#"%f", accelerometer.rawAccelerometerData.acceleration.y];
_accelZ.text = [NSString stringWithFormat:#"%f", accelerometer.rawAccelerometerData.acceleration.z];
}
But when I access _rawAccelerometerData, I get a Bad Access Exception.
My guess is that the accelerometerData object is getting destroyed. How do I keep it?

This is a multi-threading issue. (Thanks holex for pointing me in that direction.)
_rawAccelerometerData is always being updated, and the new object is replacing the old object (which gets destroyed because of ARC).
On another thread, I am accessing _rawAccelerometerData. Occasionally, when I access _rawAccelerometerData, I am accessing an older 'copy' of the object. Since that object has already been destroyed, I get a Bad Access.

Related

Accessing mutable data from a GCDWebServer request handler

I'm getting occasional crashes in my GCDWebServer handlers, which access mutable dictionaries. The GCDWebServer ReadMe says the handlers "are executed on arbitrary threads within GCD so special attention must be paid to thread-safety and re-entrancy," and I think that's my problem. Is there a best practice or recommended pattern for accessing mutable properties of the parent object from the handlers?
I don't know if I can synchronize between threads since I'm not creating the handler threads. Also, I imagine I could use an asynchronous handler, then call a method on the main thread from there, then do my work in that method, then send the response, but that seems more complicated and less efficient than necessary.
Here's a simplified version of my code:
#property (nonatomic, strong) NSMutableDictionary *data;
#property (nonatomic, strong) GCDWebServer *webServer;
- (void)setup {
self.data = [NSMutableDictionary dictionary];
[self.data setObject:#"1" forKey:#"status"];
self.webServer = [[GCDWebServer alloc] init];
[self.webServer addHandlerForMethod:#"GET" path:#"/getStatus.txt" requestClass:[GCDWebServerRequest class] processBlock:^(GCDWebServerRequest *request) {
return [self handleStatusRequest:request];
}];
}
- (GCDWebServerDataResponse *)handleStatusRequest:(GCDWebServerRequest *)request {
NSString *status = [self.data objectForKey:#"status"]; // crash here
return [GCDWebServerDataResponse responseWithText:status];
}
Are you mutating your data dictionary after creating it? If so that would explain the crashes.
You must prevent concurrent access to your data dictionary by using locks. The easiest way is through GCD e.g.
#property dispatch_queue_t lock;
__block NSString* status;
dispatch_sync(self.lock, ^{
status = [self.data objectForKey:#"status"];
});
NSString* status = #"Hello";
dispatch_async(self.lock, ^{
[self.data setObject:status forKey:#"status"];
}); // Use dispatch_sync() or dispatch_async() here depending on your needs

Object does not seem to be retained when instance variable assigned to it

I am having some sort of retention issue when I assign an object to an instance variable. It was my understanding that all local instance variables were strong so the assignment alone would be enough to make sure the object would not be destroyed as long as it was referenced by the instance variable, but this is not the case.
In the code below, I assign my instance MKRoute variable myRoute to the route from the calculate directions call. At that point myRoute exists and is valid, has steps and polyline etc. But later in my code, I attempt to access myRoute and I am getting a BAD ACCESS.
It appears to still have a memory pointer value, but the rest of the object is gone at least in the debugger. There is no polyline or steps etc. or at least not what it should be. There are no other assignments or modifications or myRoute in my code. What do I need to do to make sure that this object is not destroyed?
#implementation SomeClass
MKRoute *myRoute = nil;
.
.
.
-(void) showRoute:(MKMapItem *)origin destination:(MKMapItem*)destination
{
MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
request.source = origin;
request.destination = destination;
request.requestsAlternateRoutes = NO;
MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
if (error) {
NSLog(#"ERROR %#",error);
// Handle Error
} else {
[self drawRoute:response];
}
}];
}
-(void)drawRoute:(MKDirectionsResponse *)response
{
for (MKRoute *route in response.routes)
{
myRoute = route; // set the global route
[mapView addOverlay:route.polyline level:MKOverlayLevelAboveRoads];
}
}
.
.
.
#end
I am happy to concede its a global variable vs an instance variable, but I still don't understand why its losing its retention, if I try STATIC or __strong on the variable declaration it still winds up assigned in the drawroute and gone later.
Globals are not a typical or recommended way to store variables, and you didn't provide any information about the code that accesses this variable and the stack you get when you crash (which are critical in accurately diagnosing).
That said, your problem is likely that you are modifying this variable on one queue while accessing it on another. calculateDirectionsWithCompletionHandler: executes its completion handler on the main thread. Is your crash happening when accessing it from some other thread? Updating pointer values is not promised to be atomic, and it is possible for you to receive a garbage pointer if you're reading and updating at the same time.

EXC_BAD_ACCESS when passing NSString to method from within a block

I have a method which requests access to Twitter, and then sets the username using - (void) setUsername:(NSString *)username. I have to pass ACAccountStore.requestAccessToAccountsWithType... a completion handler (block) as follows:
[self.accounts requestAccessToAccountsWithType:twitterAccountType options:NULL completion:^(BOOL granted, NSError *actualError) {
if(granted == YES) {
self.haveTwitterAccess = YES;
ACAccount *account = [self.accounts accountsWithAccountType:twitterAccountType][0];
[self setUsername: account.username];
// [...]
When I do this, however, I get EXC_BAD_ACCESS in the setUsername method (to which I'm trying to pass account.username). If I try NSLog(#"%#", account.username); then I can see that the value is correct, but when passed to setUsername, the username variable is nil.
I imagine that it is being released in between the threads somewhere here, but am not sure, what with all this new ARC stuff, how to stop it happening. In essence, this boils down to passing strings between threads I suppose?
I've tried things like [account.username copy] and setting the accounts store as retain etc., but nothing seems to work. I've also tried referencing self using:
__block UserManager *blockSafeSelf = self;
How to I ensure that the variable passed from within a callback block to a method on another object is retained?
EDIT:
This is in a class called UserManager which has a property
#property (nonatomic) ACAccountStore *accounts;
which deals with the Twitter access. It also has the method:
- (void) setUsername: (NSString *) username;
which I'm trying to call with the account username inside the completion block. I hope that helps pinpoint the issue a bit better.
EDIT (2):
username is a property of UserManager and is declared as follows:
#property (nonatomic) NSString *username;

Access property before viewDidLoad

Inside the following method I am saving the current temp of a location, I want to be able to access the temp anywhere inside the class.
[_weatherAPI currentWeatherByCoordinate:CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude) withCallback:^(NSError *error, NSDictionary *result) {
downloadCount++;
if (downloadCount > 1)
if (error) {
}
[_locationManager stopUpdatingLocation];
self.tempLabel.text = [NSString stringWithFormat:#"%.0f°",
[result[#"main"][#"temp"] floatValue] ];
self.locationLabel.text = [NSString stringWithFormat:#"%#",
result[#"name"]
];
self.summaryLabel.text = [NSString stringWithFormat:#"%#",
result[#"weather"][0][#"main"] ];
[self setSaveTemp:result[#"main"][#"temp"]];
}];
I am then saving it to a property
#property (strong, nonatomic) NSString *saveTemp;
And then saving it to this.
-(void)setSaveTemp:(NSString *)saveTemp {
_saveTemp = saveTemp;
}
In my viewDidLoad I am then calling everything
_weatherAPI = [[OWMWeatherAPI alloc] initWithAPIKey:#""];
[_weatherAPI setLangWithPreferedLanguage];
[_weatherAPI setTemperatureFormat:kOWMTempCelcius];
NSLog(#"%#", _saveTemp);
But this returns null as the viewDidLoad is getting called before the _weatherAPI currentWeatherByCoordinate what can I do to be able make _saveTemp not equal null but the actual value?
I think this api will make server call and so it takes time to get the json data.
once json data is available, then it will execute callback block.
saveTemp variable will be initially nil, and you are setting it in completion block.
All you can do is just wait until the download is finished.And if you want to execute some method after download,then call that method in callBack block. so, that way your app can know that download is finished.

How to save data out of an iOS completion block

I'm basically implementing a fancier NSURLConnection class that downloads data from a server parses it into a dictionary, and returns an NSDictionary of the data. I'm trying add a completion block option (in addition to a delegate option), but it crashes anytime I try to store that data in another class.
[dataFetcher_ fetchDataWithURL:testURL completionHandler:^(NSDictionary *data, NSInteger error) {
contentDictionary_ = data;
}];
I can NSLog that data just fine, and basically do whatever I want with it, but as soon as I try to save it into another variable it crashes with a really obscure message.
EDIT: the crash message is EXC_BAD_ACCESS, but the stack trace is 0x00000000 error: address doesn't contain a section that points to a section in a object file.
I'm calling this function in the init method of a singleton. It DOES let me save the data if I set this in the completion block.
[SingletonClass sharedInstance].contentDictionary = data
But then the app gets stuck forever because sharedInstance hasn't returned yet, so the singleton object is still nil, so sharedInstance in the completion block calls init again, over and over.
EDIT 2: The singleton code looks like this:
+ (SingletonClass*)sharedInstance {
static SingletonClass *instance;
if (!instance) {
instance = [[SingletonClass alloc] init];
}
return instance;
}
- (id)init {
self = [super init];
if (self) {
dataFetcher_ = [[DataFetcher alloc] init];
NSString *testURL = #"..."
[dataFetcher_ fetchDataWithURL:testURL completionHandler:^(NSDictionary *data, NSInteger error) {
[SingletonClass sharedInstance].contentDictionary = data;
}];
}
return self;
}
Like I said, this works fine but repeats the initialize code over and over until the app crashes. This only happens the first time I run the app on a device, because I cache the data returned and it doesn't crash once I have the data cached. I would like to be able to just say self.contentDictionary = data, but that crashes.
Specify a variable to be used in the block with the __block directive outside of the block:
__block NSDictionary *contentDictionary_;
[dataFetcher_ fetchDataWithURL:testURL completionHandler:^(NSDictionary *data, NSInteger error) {
contentDictionary_ = data;
}];
You're invoking recursion before ever setting the "instance". (which I now see you understand from OP).
In your block, you can use the ivar or an accessor instead of
[SingletonClass sharedInstance].contentDictionary
use:
_contentDictionary = [data copy]; or self.contentDictionary=data;
assuming that the ivar backing the contentDictionary property is _contentDictionary.
It sounds like you tried self.contentDictionary and it failed? I got it to work in a test, with ARC turned, so there may be something about your dataFetcher that is affecting this. In my test dataFetcher just returns a dictionary with a single element.
Turns out the issue was with a bunch of different parts. My URL was empty sometimes, and my data fetcher would just fail immediately and call the completion block. In my completion block I hadn't included any error handling, so if the singleton class hadn't initialized, it would repeat forever. With a real URL this doesn't happen.
I still would like to figure out why it crashes when I try to assign the data to an ivar, though.

Resources