Random EXC_BAD_ACCESS (code = 1, address = 0x4) with multithreading on iOS

I have a case where I get an exception EXC_BAD_ACCESS (code=1, address=0x4)at random when calling a method with multiple threads. The same method, once cheated on mistakes, sometimes not. But my measurements show that at least 1 out of 5 runs will produce this error.

First, let me show you a diagram of how my code is organized, so it will be easier to read the code.

enter image description here

View, (, AppDelegate) . CompleteHandler, . , ViewControllers.

ViewController , .

, , . , ViewController, .

, , , - .

Zombies, .

:


ViewController

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    // THIS IS NOTIFICATIONS FROM MODEL
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(modelProgressUpdate:) name:ProcessUpdateNotification object:nil];

    self.coreDataQueue = dispatch_queue_create("com.my.core.data", NULL);

    [self download1];
}

#pragma mark - Progres bar

// METHOD THAT IS CALLED AFTER NSNOTIFICATION IS CALLED
- (void) modelProgressUpdate:(NSNotification * ) notification{
    BINotificationsProcessUpdate * update = (BINotificationsProcessUpdate *   )notification.object;

    [self updateProgressWithPercent:update.percent];
}

- (void) modelPrograssCancel
{
    [self.progressViewContainer layoutSubviews];
    [UIView animateWithDuration:0.5 animations:^{
        self.ProgressViewTopConstraint.constant = -15;
        [self.progressViewContainer layoutSubviews];
        [self.view layoutSubviews];
    }];
    self.ProgressView.progress = 0;
}


// METHODS THAT ARE CALLED WITH SERVER DELEGATES
- (void) cancelProgresView
{
    [self modelPrograssCancel];
}

- (void) progressInProcentage1:(float)procentage{

    __weak ViewController * weekSelf = self;
    dispatch_async(dispatch_get_main_queue(), ^(void) {
        [weekSelf updateProgressWithPercent:procentage];
    });
}

- (void) progressInProcentage2:(float)procentage{    
    __weak ViewController * weekSelf = self;
    dispatch_async(dispatch_get_main_queue(), ^(void) {
        [weekSelf updateProgressWithPercent:procentage];
    });
}


- (void) updateProgressWithPercent: (float) percent
{
    if (self.ProgressViewTopConstraint.constant != 0) {
        [self.progressViewContainer layoutSubviews];
        [UIView animateWithDuration:0.5 animations:^{
            self.ProgressViewTopConstraint.constant = 0;
            [self.progressViewContainer layoutSubviews];
            [self.view layoutSubviews];
        }];
    }
    self.ProgressView.progress = percent;
}

#pragma mark - download data
-(void)download1{

    __weak ViewController * weekSelf = self;
    // build new NSManagedObjectContext
    weekSelf.backgroundManagedObjectContext = [[NSManagedObjectContext alloc]init];
    [weekSelf.backgroundManagedObjectContext setPersistentStoreCoordinator:[[ManagedContextHelper getManagedObjectContext] persistentStoreCoordinator]];
    [[NSNotificationCenter defaultCenter] addObserver:weekSelf selector:@selector(backgroundContextDidSave:) name:NSManagedObjectContextDidSaveNotification  object:weekSelf.backgroundManagedObjectContext];

    AppDelegate * appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    [appDelegate.serverConnection callGet1:weekSelf.server managedObjectContext:weekSelf.backgroundManagedObjectContext delegate:weekSelf  withCompletionHandler:^(BIResponseObject *response) {
        switch (response.responseType) {
            case BIResponseTypeNoInternet:
            case BIResponseTypeConnectionTimeOut:
                [weekSelf cancelProgresView];
                break;
            case BIResponseTypeOK:
                [weekSelf download2];
                break;

            default:
                [weekSelf loadModel];
                [weekSelf noServerWarrning];
                break;
        }
    }];
}

-(void)download2{

    __weak ViewController * weekSelf = self;
    AppDelegate * appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    [appDelegate.serverConnection callGet2:weekSelf.server managedObjectContext:weekSelf.backgroundManagedObjectContext delegate:weekSelf withCompletionHandler:^(BIResponseObject *response) {

        switch (response.responseType) {
            case BIResponseTypeFactTableOK: {
                weekSelf.object.downlodedDate = [NSDate date];
                [weekSelf.object save:weekSelf.backgroundManagedObjectContext];
                [weekSelf loadModel];
                [[ManagedContextHelper getManagedObjectContext] save:nil];
            }
            case BIResponseTypeConnectionTimeOut:
            case BIResponseTypeNoInternet:
            case BIResponseTypeFactTableError:
                [weekSelf cancelProgresView];
                break;
            default:
                [weekSelf cancelProgresView];
                break;

        }
    }];
}

#pragma mark - LOAD MODEL

-(void)loadModel{

    __weak ViewController * weekSelf = self;

    dispatch_async(weekSelf.coreDataQueue, ^(void){

        // build new NSManagedObjectContext
        if (!weekSelf.backgroundManagedObjectContext) {
            weekSelf.backgroundManagedObjectContext = [[NSManagedObjectContext alloc]init];
            [weekSelf.backgroundManagedObjectContext setPersistentStoreCoordinator:[[ManagedContextHelper getManagedObjectContext] persistentStoreCoordinator]];
            [[NSNotificationCenter defaultCenter] addObserver:weekSelf selector:@selector(backgroundContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:weekSelf.backgroundManagedObjectContext];
        }


        MyUIObject * uiObject = [DataBase getUIObject:weekSelf.backgroundManagedObjectContext];

        weekSelf.myObject = [[UIScrollObject alloc] initWith: uiObject];
        weekSelf.myObject.delegate = weekSelf;

        if (uiObject && weekSelf.myObject) {
            dispatch_async(dispatch_get_main_queue(), ^(void) {

                self.myObject.translatesAutoresizingMaskIntoConstraints = NO;
                [self.view addSubview:self.myObject];

                [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_myObject]|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(_myObject)]];
                [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(66)-[_myObject]|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(_myObject)]];

                [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.myObject attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:1 constant:0]];

               [self.view sendSubviewToBack:self.myObject];



               // Add model
               [self.myObject createModel]; // THIS IS A LINE THAT I THINK IT BREAK,... logs are till here.
            });
        }
        else {
            [weekSelf dissmisView:weekSelf];
        }
    });
}

- (void) dissmisView: (ViewController * ) selfReference  {
    dispatch_async(dispatch_get_main_queue(), ^(void) {
        [selfReference cancelProgresView];
        if (![selfReference.presentedViewController isBeingDismissed]) {
            [selfReference dismissViewControllerAnimated:YES completion:nil];
        }
    });
}

#pragma mark - handling multi ManagedObjectContext

- (void)backgroundContextDidSave:(NSNotification *)notification {
    /* Make sure we're on the main thread when updating the main context */
    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(backgroundContextDidSave:)
                           withObject:notification
                        waitUntilDone:NO];
        return;
    }

    /* merge in the changes to the main context */
    [[ManagedContextHelper getManagedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
}

...
@end

dispatch_async(self.model_queue, ^(void){
    // Create Model in new thread
    [self createPrimaryModel_newThread]; // inside here notifications are send to update progress bar. 

    /// Model is complete
    [BINotifications notifyModelIsCompletedCreating:self];
});

BINotifications

+ (void) notifyProgressUpdateWithPercent: (float) percent andNotificationString: (NSString * ) notificationString {
    dispatch_async(dispatch_get_main_queue(), ^(void) {
        [[NSNotificationCenter defaultCenter] postNotificationName:ProcessUpdateNotification
                                                        object:[[BINotificationsProcessUpdate alloc]
                                                                initWithPercent:percent
                                                                description:notificationString]];
    });
}

-(void)callGet2: (Server *) server managedObjectContext: (NSManagedObjectContext *)managedObjectContext delegate: (id) delegate withCompletionHandler:(void (^)(BIResponseObject * response))callback
{
    id<BIServerConnectionDelegate> _delegate = delegate;
    [_delegate progressInProcentage2:0.0];
    NSString * method = [NSString stringWithFormat:@".../%i", ...];
    [self callServerWithMethod:method server:server withCallBack:^(BIResponseObject *response) {
        [_delegate progressInProcentage2:0.3];
        // parse
        response.responseType = [self parse2:response server:server managedObjectContext:managedObjectContext delegate:_delegate];
        [self setResponseTitleAndMessage:response server:server];
        callback(response);
    }];
}

, , .

"", ... ( .), , : D, .

+4

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


All Articles