EXC_BAD_ACCESS when passing NSString to method from within a block - ios

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;

Related

Set NSObject property - EXC_BAD_ACCESS

Process :
App is on the home view controller and is requesting data on API to set an NSObject property. The request is processing on a private method.
User change the view controller to a second view controller (the request is still processing asynchronously)
The second view controller is loaded
The request is ending and app return EXC_BAD_ACCESS when it setting the object property
It seems the object has not the correct memory access.
I would like than the user can switch view controller, even if there is a request pending, and the application doesn't crash.
I don't want to block the user on the view controller during loading.
User.h
#interface User : NSObject
[...]
#property (nonatomic) NSString *notification;
[...]
-(void) methodName;
#end
User.m
-(void) methodName{
//there is code around but useless for this problem
[...]
NSError *error;
NSMutableDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
self.notification = [[dict objectForKey:#"infos"] objectForKey:#"notification"]; //Here is the EXC_BAD_ACCESS
[...]
}
MyController.m
#interface MyController ()
#end
User *user;
#implementation HomeCVViewController
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:YES];
user = [User new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[user someMethod];
dispatch_async(dispatch_get_main_queue(), ^{
[...]//some code
});
});
}
#end
EDIT :
I just put #property (nonatomic) User *user; in MyController.h and remove User *user; in MyController.m . The user is not deallocated and there is no crash.
Verify that [dict objectForKey:#"infos"] is not NSNull - Crash
can be here.
Other code looks OK.
Also add -(void)deallocto your object and put a
break point there to verify that the object is not being released
before the assignment.
Check your output at the NSError
NSError *error;
NSMutableDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
NSLog(#"%#", error.localizedDescription); <----- HERE
self.notification = [[dict objectForKey:#"infos"] objectForKey:#"notification"];
When you try to convert from/to JSON, improper JSON structure will cause it to crash. See the error for more information.
Usually, this type of error appears when object deallocated but your code try to access it's data
so, first of all, check how do you hold your User entity in memory, throw, for example, property:
#property (nonatomic, strong) User *u;
and make sure it is still in memory when operation completed:
Hope this will help you.
Instead of
self.notification = [[dict objectForKey:#"infos"] objectForKey:#"notification"]; //Here is the EXC_BAD_ACCESS
write
NSDictionary* infoDict = dict [#"infos"];
id notification = infoDict [#"notification"];
self.notification = notification;
set a breakpoint, and examine each of infoDict, notification, and self. That takes the guesswork out of it. Right now you don't even know which of these three assignments goes wrong.
And since you have lots of lines of code that are in your opinion bug free and irrelevant, chances are good that the actual bug is hiding somewhere in those lines. Remember: There is a bug in your code. Assuming that any of your code is bug free is daft, because you already know it isn't!

Unable to access static variables from NSObject class but I am able from UIViewController

Ok, strange thing occurred and I guess answer is quite simple, but I fail to figure out what's going on.
Situation is next:
I have an NSObject class called Constants.
Constants.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <GooglePlus/GooglePlus.h>
#interface Constants : NSObject
+(Constants*)shared;
#property GTLPlusPerson* googlePlusUser;
#property int profileType;
#property NSString *userName, *userLastName, *userEmail, *userGoogleId,*userProfilePicture;
#end
Constants.m
#import "Constants.h"
#implementation Constants
#synthesize profileType, userProfilePicture, userLastName,userName,userGoogleId,userEmail;
static Constants *constants = nil;
+ (Constants*)shared {
if (nil == constants) {
constants = [[Constants alloc] init];
}
return constants;
}
I use this class in order to save some static variables that I will use throughout the app.
Now, If I try and declare one of the variables like
[Constants shared].userName = #"name";
from an NSObject class method (Which I call from a ViewController), I fail to do so.
But If I declare Constant variables directly from ViewController (after viewDidLoad for example) everything works fine.
Here is the Class I try to declare variables from, but I fail (It also has singleton in it, that might be the source of the problem, but im not sure why would it)
#implementation GoogleLogin
static GoogleLogin* gLogin = nil;
+(GoogleLogin*)shared
{
if (nil == gLogin){
gLogin = [[[self class]alloc]init];
}
return gLogin;
}
-(void)getProfile
{
GTLServicePlus* plusService = [[GTLServicePlus alloc] init];
plusService.retryEnabled = YES;
[plusService setAuthorizer:[GPPSignIn sharedInstance].authentication];
GTLQueryPlus *query = [GTLQueryPlus queryForPeopleGetWithUserId:#"me"];
plusService.apiVersion=#"v1";
[plusService executeQuery:query
completionHandler:^(GTLServiceTicket *ticket,
GTLPlusPerson *person,
NSError *error) {
if (error){
NSLog(#"Error while fetching user profile: %#", error);
}else{
NSLog(#"User profile information fetched OK");
[Constants shared].googlePlusUser = person;
[Constants shared].profileType = 1;
[Constants shared].userName = person.name.givenName;
[Constants shared].userLastName = person.name.familyName;
[Constants shared].userEmail = [GPPSignIn sharedInstance].authentication.userEmail;
[Constants shared].userGoogleId = person.identifier;
[Constants shared].userProfilePicture = person.image.url;
NSLog(#"%# %# %# %# %# ",person.name.givenName,person.name.familyName,[GPPSignIn sharedInstance].authentication.userEmail,person.identifier,person.image.url);
}
}];
}
and this is how I call those methods, from my ViewController:
- (IBAction)signupWithGoogle:(UIButton *)sender {
//if i call this method here, on button click, it will finish all the steps needed, except setting constant variables
[[GoogleLogin shared] googleLoginFromViewController:self];
//if I uncomment next line, username will be declared and I will be able to access it later
//[Constants shared].userName = #"Petar";
}
Can anybody figure out why is this happening and what should I do to change that?
When you define a property is strongly suggested to declare the attributes to use with it. I guess the compiler should complain about this with a message like
No 'assign', 'retain', or 'copy' attribute is specified - 'assign' is
assumed
So, use the following instead (copy semantics is fine for mutable classes).
#property (nonatomic, copy) NSString *myString;
You should also specify if the property should be accessed in a atomic or nonatomic way. If you don't specify it, the former will be applied.
Then, you are using a singleton pattern. The suggested way is to use GCD like so.
+ (ConstantsManager*)sharedManager {
static ConstantsManager *sharedManager;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedManager = [[[self class] alloc] init];
});
return sharedManager;
}
Well you did not set your property attributes on the singleton class.
For example,
#property (nonatomic, strong, readonly) ...
Have you tried moving the property assignments out of the completionHandler? It may be that your properties are being assigned on a background thread and your view controller is not catching the assignment. An easy way to check is to override the setters and getters and put breakpoints in them to see what order they are being accessed.
1) Remove the #synthesize because it's not needed (properties will be synthesized as _property automatically)
2) Override setter & getter
-(void)setProfileType:(NSInteger)profileType {
_profileType = profileType;
}
-(NSInteger)profileType {
return _profileType;
}
3) Place breakpoints within these methods and see if the getter is being called before the setter. Alternatively, if simply moving the assignments out of the completionHandler fixes it you know you have some concurrency issues.
I suggest reading up on atomic/nonatomic properties, #synthesize and Objective-C concurrency.

Pass by Reference in Callbacks

I am facing a problem. I am passing an object to another class by its reference & setting the value in that object. Now when I access this variable in callback handler then It is nil.
My sample code is:
Class A:
__block NSString *getListJobId = nil;
ClassB *bobject = [[ClassB alloc]init];
[bobject getItemsWithJobId:&getListJobId onSuccess:^(NSArray *response) {
NSLog(#"job id %#",getListJobId); //It is nil, It should be **shiv**
} onFailure:^(NSError *error) {
}];
Class B:
.h
- (void)getItemsWithJobId:(NSString **)jobId onSuccess:(void (^)(NSArray *))completedBlock onFailure:(void (^)(NSError *))failureBlock;
.m
- (void)getItemsWithJobId:(NSString **)jobId onSuccess:(void (^)(NSArray *))completedBlock onFailure:(void (^)(NSError *))failureBlock
{
*jobId = #"shiv";
completedBlock([NSArray new]);
}
I am getting this jobId nil in class A in callback response. How can I get this value from class B to class A.
I will appreciate your help.
You should not pass by reference to get an updated value in the method, because the getListJobId at ClassA and ClassB do not point same address.
An Obj-C block capture the value of variables outside of its enclosing scope.
See "Blocks Can Capture Values from the Enclosing Scope" section.
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html
Instead of passing by reference, we can get the updated value from arguments of the block and update getListJobId in the block.
Class A:
__block NSString *getListJobId = nil;
ClassB *bobject = [[ClassB alloc] init];
[bobject getItemsWithJobId:getListJobId onSuccess:^(NSArray *response, NSString *updatedJobId) {
getListJobId = updatedJobId;
NSLog(#"job id %#", getListJobId); // job id **shiv**
} onFailure:^(NSError *error) {
}];
Class B: .h
- (void)getItemsWithJobId:(NSString *)jobId onSuccess:(void (^)(NSArray *, NSString *))completedBlock onFailure:(void (^)(NSError *))failureBlock;
.m
- (void)getItemsWithJobId:(NSString *)jobId onSuccess:(void (^)(NSArray *, NSString *))completedBlock onFailure:(void (^)(NSError *))failureBlock
{
NSString *updatedJobId = #"**shiv**";
completedBlock([NSArray new], updatedJobId);
}
Taking the address of a __block variable does not always do what you expect.
In the current implementation, __block variables are initially allocated on the stack, and then "moved" to the heap upon any of the blocks that use it being moved to the heap (which is caused by the block being copied).
Therefore, the address of a __block variable changes over its lifetime. If you take the address of it, and it moves, then you will no longer be pointing to the version of the variable that everyone else is using.
Here, what is happening is that you take the address of the __block variable getListJobId while it is still on the stack. It is still on the stack at that point because it is caused to be moved to the heap by the copying of any block that uses it, but no block has been created at the point yet.
Then, a block that uses getListJobId gets copied somewhere and getListJobId gets moved to the heap. Exactly where this happens is not very clear, because ARC is allowed to insert copies of blocks in various places. Plus, the code you are showing here does not seem like your real code, because there would be no point to calling a "completion block" at the end of a method synchronously (in that case you would just return and let the caller perform the operations they want when completed). Rather, your real code probably performs an asynchronous operation, at the end of which the completion handler is called. dispatch_async and related asynchronous functions copy the blocks passed to them (which in turn copy any blocks captured, and so on).
I am guessing that in your real code, both the *jobId = #"shiv"; line and the calling of the completion block happen in the asynchronous operation. What is happening is that the creation of the asynchronous operation copies the block and causes getListJobId to be moved to the heap. So inside the asynchronous operation, getListJobId refers to the heap version of the variable. However, the *jobId = #"shiv"; writes to the stack version of the variable, because jobId is a pointer taken from the address of the variable when it was still on the stack. So you are writing to and reading from different variables.
Furthermore, what you are doing in *jobId = #"shiv"; is very dangerous, because by the time of the asynchronous operation, the stack frame of the original function call no longer exists. And writing to a variable on the stack after the stack frame is gone is undefined behavior, and you may be overwriting other unknown variables in memory. You are lucky it didn't crash.

How do I keep a parameter passed into a block

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.

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