Search code examples
iosobjective-cuiviewcontrolleruicontainerviewparentviewcontroller

Container View Controller - sending data from child to parent failed


I create my Custom Container View Controller according to Apple’s guide, which means I use no segue. To send data from my ChildVC to my ParentVC I use the following schema.

In ChildVC.h

typedef void (^ActionBlock)(NSUInteger);

@property (nonatomic, copy) ActionBlock passIndexToParent;

Then in ParentVC.m viewDidLoad

childVC.passIndexToParent=^(NSUInteger imageIndex){
//do some cool stuff
}

ChildVC contains a TableView and the button(s) to be clicked is in a cell in the table so I use the following technique to get the index of the row clicked

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    //…

     //target action
    [cell.myImageButton addTarget:self action:@selector(getIndexOfTapped:) forControlEvents:UIControlEventTouchUpInside];
    //…
    return cell;
}


- (void) getIndexOfTapped:(id)sender
{
    NSLog(@“X image tap confirmed");
    CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
    if (indexPath != nil)
    {

        NSLog(@"INDEX of X image tapped is %ld",(long)indexPath.row);
        self.passIndexToParent(indexPath.row);//Thread 1: EXC_BAD_ACCESS(code=1, address=0X10)
    }
}

Now when I run the program I get

Thread 1: EXC_BAD_ACCESS(code=1, address=0X10)

for line

self.passIndexToParent(indexPath.row);

No further error data. Any help fixing this problem?


Solution

  • I would highly advise against using this pattern. Primarily because it's too easy to create a retain cycle. If the block you pass to the child contains a strong reference to the parent, then you won't be able to break the retain cycle unless you manually nil out the child's block when dismissing the parent.

    Instead, you should create a delegate protocol on the child. It would look something like this:

    //ChildVC.h
    
    @class ChildVC
    
    @protocol ChildVCDelegate <NSObject>
    
    -(void)childVC:(ChildVC *)childVC didSelectIndex:(NSUInteger)index;
    
    @end
    
    @interface ChildVC
    
    ...
    
    @property (nonatomic, weak) id<ChildVCDelegate> delegate;
    
    ...
    
    @end
    
    //ChildVC.m
    
    if (indexPath != nil)
    {
        if ([self.delegate respondsToSelector:@selector(childVC:didSelectIndex:)]) {
            [self.delegate childVC:self didSelectIndex:indexPath];
        }
    }
    

    Then define the -(void)childVC:(ChildVC *)childVC didSelectIndex:(NSUInteger)index; method in the parent and make sure to set the parent as the delegate of the child.

    See also: The #1 question on frequent iOS questions

    But, sigh, to answer your question, you are sometimes calling the block when you haven't set it. You need to nil check before you call blocks.