1

I am trying to implement this answer posted on StackOverflow

use block to pass data from modal view to parent view

When I try to set vale to block in my implementation of SecondViewController(Modal view). I receive an error of bad memory access.

I am still learning Objective C and using protocol I am able to do this but block seems more effective. Could anyone please tell me where am I making mistake. Here are the codes and images of my StoryBoard.

FirstViewController.h

#import <UIKit/UIKit.h>
#import "SecondViewController.h"

@interface FirstViewController : UIViewController

@end

FirstViewController.m

#import "FirstViewController.h"
#import "SecondViewController.h"

@interface FirstViewController ()

@end

@implementation FirstViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    SecondViewController *second = [[SecondViewController alloc]init];
    second.somethingHappenedInModalVC = ^(NSString *response) {
        NSLog(@"Something was selected in the modalVC, and this is what it was:%@", response);
    };

}

@end

SecondViewController.h //modal view

#import <UIKit/UIKit.h>
#import "FirstViewController.h"

@interface SecondViewController : UIViewController
@property (nonatomic, copy) void (^somethingHappenedInModalVC)(NSString *response);

@end

SecondViewController.m

 #import "SecondViewController.h"


@interface SecondViewController ()
@property (strong, nonatomic) IBOutlet UITextField *textField;

@end

@implementation SecondViewController

- (IBAction)closeView:(id)sender {
    self.somethingHappenedInModalVC(@"Sending the message"); //here memory warning display.
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end

enter image description here

Community
  • 1
  • 1
Alok
  • 119
  • 1
  • 9

2 Answers2

3

You have a viewDidLoad that does the following:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    SecondViewController *second = [[SecondViewController alloc]init];
    second.somethingHappenedInModalVC = ^(NSString *response) {
        NSLog(@"Something was selected in the modalVC, and this is what it was:%@", response);
    };
}

That creates a SecondViewController, sets the somethingHappenedInModalVC property, but then lets that second view controller instance fall out of scope and will be therefore be deallocated (thus discarding the block property also).

Thus, when you finally transition to SecondViewController (at which point another instance of this view controller is instantiated), the somethingHappenedInModalVC for this new instance is nil, and thus an attempt to call that block will crash.

You generally set properties in this destination controller in prepareForSegue (or if you're manually instantiating the SecondViewController, manually setting it there). You haven't shown us how you're transitioning to this second view controller, so we can't tell you which way is correct in your example.

For example, you might have a prepareForSegue in your first view controller like so, which is called automatically when you perform segue to the SecondViewController:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([[segue identifier] isEqualToString:@"IdForSegue"]) {
        SecondViewController *destination = segue.destinationViewController;
        destination.somethingHappenedInModalVC = ^(NSString *response) {
            NSLog(@"Something was selected in the modalVC, and this is what it was:%@", response);
        };
    }
}

Note, that assumes you've supplied a storyboard ID to the segue that does the transition to the second view controller. Make sure that the identifier in the code snippet matches the storyboard ID you gave to the segue in the storyboard.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Since I have declared block inside SecondViewController (modal view). There I am dismissing it using IBAction, will prepareForSegue of that class be called before view get dismissed. Which of the view controller (from two above) segue method shall I implement to pass this block variable and how. – Alok Nov 06 '15 at 20:09
  • The `prepareForSegue` is implemented in the first view controller and is called when the first view controller segues to the second one. So we set the block property when you segue from the first to the second and thus that property will be available by the time the second view controller's `IBAction` is called. This assumes, of course, that you set the storyboard ID for the segue properly, though. – Rob Nov 06 '15 at 20:13
2

The issue is, you are creating a new instance of SecondViewController and setting the block to newly created instance, not to the actual instance that is currently(going to be) displayed. So in your actual SecondViewController object the block will be still nil and that's the reason why it is crashing.

When dealing with blocks you should always do a nil check before invoking it:

if (somethingHappenedInModalVC)
{
   somethingHappenedInModalVC(@"Sending the message");
}

And for solving the logical issue, you should use the actual instance of SecondViewController rather than creating a new instance in the viewDidLoad.

Midhun MP
  • 103,496
  • 31
  • 153
  • 200