ARC: getting EXC_BAD_ACCESS from the inner block used in the delegation method

I should be doing something wrong, but the Automatic Reference Counting docs don't give me clues about what this might be. What I'm doing is a method call with a block callback from the delegate method. Access to the same delegate from within the block results in poor access . The problem is that the object I'm passing in - loginController , which sends a message to its delegate - is obviously not released, when I cannot access it inside the block, I can call this method several times without a question. Here is my code:

- (void)loginViewDidSubmit:(MyLoginViewController *)loginController { NSString *user = loginController.usernameLabel.text; NSString *pass = loginController.passwordLabel.text; __block MyLoginViewController *theController = loginController; [self loginUser:user withPassword:pass callback:^(NSString *errorMessage) { DLog(@"error: %@", errorMessage); DLog(@"View Controller: %@", theController); // omit this: all good theController = nil; }]; } 

NSZombieEnabled does not register anything and there is no useful stack trace from gdb. What am I doing wrong here? Thanks for any pointers!


Edit:

I realized that the problem is larger - the callback above is called from the NSURLConnectionDelegate method (the block itself is a strong property for this delegate, so ARC should call Block_copy ()). Do I need to take special measurements in this scenario?

Flow (loginController remains visible all the time):

LoginController

 [delegate loginViewDidSubmit:self]; 

View Delegate

 (method shown above calls the loginUser: method, which does something like:) httpDelegate.currentCallback = callback; httpDelegate.currentConnection = // linebreak for readability [[NSURLConnection alloc] initWithRequest:req delegate:httpDelegate startImmediately:YES]; 

NSURLConnectionDelegate

 - (void)connection:(NSURLConnection *)aConnection didFailWithError:(NSError *)error { if (NULL != currentCallback) { currentCallback([error localizedDescription]); self.currentCallback = NULL; } } 

And here I get bad access, but ONLY if I get access to this loginController variable ...

+2
source share
3 answers

Set the copy attribute for the property, or simply call the copy method on the block.

 - (void)loginUser:(NSString *)user withPassword:(NSString *)pass callback:(void (^callback)(NSString *)) { callback = [callback copy]; 
+3
source

The actual solution was that I had a block as a strong property, but that should have been a copy property! D'o!


The first "decision":

I just found a way to prevent bad access. As shown in my Editing above, the View delegate forwards the block to httpDelegate (an instance of another class), which in turn holds a link to the block. Assigning a block to a temporary variable and sending a temporary variable to the block solves the problem for any reason. So:

This leads to block execution failures as described

 httpDelegate.currentCallback = callback; 

It works

 MyCallbackType aCallback = callback; httpDelegate.currentCallback = aCallback; 

I agree with this as an answer, if someone has more information, I will gladly reconsider my decision. :)

+1
source

I understand that it happens that loginController is dead right after calling its delegate. Therefore, a failure occurs. Without additional information, I can only think of possible scenarios:

  • The loginController object (modifier of type __block) is not stored in the block. If the block runs asynchronously, the loginController may not be available if it was killed by elsewere. Therefore, no matter what you want to do with it, you will not be able to access it inside the block, and the application will crash. This can happen if the controller was killed after sending loginViewDidSubmit.

  • I think that most likely this could be your situation: loginController calls its delegation object. The delegate method ends with a synchronous call to the callback block, which kills the controller. The controller is expected to be available after calling the delegate method. Killing him in the delegate method is likely to lead to crashes. To make sure this is a problem, just run the loginController in the delegate method and put the NSLog statement in the controller after the delegate is called, not to mention the block, you will crash there.

Perhaps if you embed the code, we can help more.

My best.

0
source

Source: https://habr.com/ru/post/948863/


All Articles