samedi 21 novembre 2015

UI Stucks in iOS app possible reason is FMDB query?

my apps UI stuck for several seconds for the very first time app launches (MenuPage) and when I move to Account page. It stuck while calculating some status. But I did them in background queue. I paused and looked at the debugger. Its shows this:

enter image description here

I am giving the codes from account page as it is much clean than the menu page.

From account page I am calling SyncStatus like this:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [_syncStatus startStatusCalculating];
});

SyncStatus.h is:

#import <Foundation/Foundation.h>
#import "SyncStatusCalculatorOperation.h"

@interface SyncStatus : NSObject <SyncStatusOperationDelegate>

@property (nonatomic, strong) NSDictionary* syncStatusValues;
@property (nonatomic, strong) NSOperationQueue* syncStatusOperationQueue;

-(void) startStatusCalculating;
-(void) stopStatusCalculating;
-(void) forceCalculation;
-(void) startUpdateOperation;

@end

SyncStatus.m is:

#import "SyncStatus.h"
#import "SyncStatusCalculatorOperation.h"

@implementation SyncStatus {
    NSTimer *calculatorTimer;
    NSInteger cycleCount;
}

@synthesize syncStatusValues = _syncStatusValues;
@synthesize syncStatusOperationQueue = _syncStatusOperationQueue;

- (instancetype)init
{
    self = [super init];
    if (self) {
        cycleCount = 0;
        _syncStatusOperationQueue = [[NSOperationQueue alloc] init];
        _syncStatusOperationQueue.maxConcurrentOperationCount = 1;
    }
    return self;
}

-(void) forceCalculation {
    [self transmitData];
}

-(void) updateSyncStatusValues:(NSDictionary *) syncValues {
    self.syncStatusValues = syncValues;
    [self transmitData];
}

-(void) transmitData {
    if([self.syncStatusValues objectForKey:TOTAL_ROW] == nil){
        NSLog(@"no status info to send");
        return;
    }

    [[NSNotificationCenter defaultCenter] postNotificationName:SYNC_STATUS_NOTIFICATION
                                                        object:nil
                                                      userInfo:self.syncStatusValues];
}

-(void) syncCalc {
    if (cycleCount < 3) {
        [self transmitData];
        cycleCount ++;
    }
    else {
        [self startUpdateOperation];
        cycleCount = 0;
    }
}

-(void) startUpdateOperation
{
    SyncStatusCalculatorOperation *syncOperation = [[SyncStatusCalculatorOperation alloc] initWithDelegate:self];
    [self.syncStatusOperationQueue addOperation:syncOperation];
}

-(void) startStatusCalculating {

    if (![calculatorTimer isValid]) {
        cycleCount = 3;
        calculatorTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(syncCalc) userInfo:nil repeats:YES];
        [calculatorTimer fire];
    }
}

-(void) stopStatusCalculating {
    if([calculatorTimer isValid]){
        [calculatorTimer invalidate];
    }
    [self.syncStatusOperationQueue cancelAllOperations];
}

@end

SyncStatusCalculatorOperation.h:

#import <Foundation/Foundation.h>

@protocol SyncStatusOperationDelegate <NSObject>

-(void) updateSyncStatusValues:(NSDictionary *) syncValues;


@end


@interface SyncStatusCalculatorOperation : NSOperation

@property (nonatomic, assign) id <SyncStatusOperationDelegate> delegate;

- (id) initWithDelegate:(id<SyncStatusOperationDelegate>) theDelegate;

@end

And SyncStatusCalculatorOperation.m is this:

#import "SyncStatusCalculatorOperation.h"
#import "IssMANAppDelegate.h"
#import "MediaDAO.h"
#import "ISSManDBManager.h"

@implementation SyncStatusCalculatorOperation {
    BOOL isCalculating;
}

- (id) initWithDelegate:(id<SyncStatusOperationDelegate>) theDelegate {
    if(self = [super init]) {
        self.delegate = theDelegate;
        isCalculating = NO;
    }
    return self;
}
- (void)main {

    // 4
    @autoreleasepool {

        if (self.isCancelled == NO){
            [self updateStatus];
        }

    }
}

-(void)updateStatus {

    if(isCalculating)return;

    isCalculating = YES;
    NSMutableArray *tableList = [[NSMutableArray alloc] init];

    [[IssMANAppDelegate appDelegate].dbQueue inDatabase:^(FMDatabase *db) {
        FMResultSet *resultsSet = [db executeQuery:@"SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"];
        while ([resultsSet next]){
            [tableList addObject:[resultsSet stringForColumn:@"name"]];
        }
        [resultsSet close];
    }];

    __block int totalRow = 0;
    __block int pendingRow = 0;
//    totalPhotoRow = 0;
    __block int pendingPhotoRow = 0;
    __block int pendingDataDownload = 0;
    __block int pendingPhotoDownload = 0;

    pendingPhotoRow = [MediaDAO getPendingUploadImages];
    for(int k=0;k<tableList.count;k++) {

        NSString* tableName = (NSString*) [tableList objectAtIndex:k];

        if([tableName isEqualToString:@"settings"])continue;
        if([tableName isEqualToString:@"sqlite_sequence"])continue;
        if([tableName isEqualToString:@"temp_purchased_transactions"])continue;
        if([tableName isEqualToString:@"latest_project_report_issue"])continue;
        if([tableName isEqualToString:@"role"])continue;
        if([tableName isEqualToString:@"event_type"])continue;
        if([tableName isEqualToString:@"rel_role_event_type_notification_type"])continue;
        if([tableName isEqualToString:@"rel_user_product"])continue;
        if([tableName isEqualToString:@"media_content"])continue;

        [[IssMANAppDelegate appDelegate].dbQueue inDatabase:^(FMDatabase *db) {
            NSString* sql = NULL;
            if([ISSManDBManager columnExists:@"status" inTableWithName:tableName andDB:db]) {

                sql = [NSString stringWithFormat:@"SELECT COUNT(*) C FROM %@ where status <> 'deleted'",tableName];
            }
            else if([ISSManDBManager columnExists:@"sender_status" inTableWithName:tableName andDB:db]) {

                sql = [NSString stringWithFormat:@"SELECT COUNT(*) C FROM %@ where sender_status <> 'deleted'",tableName];
            }
            else {

                sql = [NSString stringWithFormat:@"SELECT COUNT(*) C FROM %@",tableName];
            }

            FMResultSet *resultSet = [db executeQuery:sql];

            while([resultSet next]) {
                int rowCount = [resultSet intForColumn:@"C"];
                if([tableName isEqualToString:@"media_content"]) {
//                    totalPhotoRow += rowCount;
                }
                else {
                    totalRow += rowCount;
                }
            }

            if([ISSManDBManager columnExists:@"status" inTableWithName:tableName andDB:db]) {
                sql = [NSString stringWithFormat:@"SELECT COUNT(*) C FROM %@ WHERE is_dirty = 1 and status <> 'deleted'",tableName];
            }
            else if([ISSManDBManager columnExists:@"sender_status" inTableWithName:tableName andDB:db]) {
                sql = [NSString stringWithFormat:@"SELECT COUNT(*) C FROM %@ WHERE is_dirty = 1 and sender_status <> 'deleted'",tableName];
            }
            else {
                sql = [NSString stringWithFormat:@"SELECT COUNT(*) C FROM %@ WHERE is_dirty = 1",tableName];
            }
            resultSet = [db executeQuery:sql];

            while([resultSet next]) {
                NSInteger rowCount = [resultSet intForColumn:@"C"];
                pendingRow += rowCount;
            }
            [resultSet close];
        }];
    }

    [[IssMANAppDelegate appDelegate].dbQueue inDatabase:^(FMDatabase *db) {

        NSString* sql = NULL;
        sql = [NSString stringWithFormat:@"SELECT COUNT(media.pk_id) C \
               FROM media, media_content \
               WHERE \
               media.pk_id = media_content.media_pk_id AND \
               media_owner IN (SELECT pk_id FROM user) AND \
               media.status = 'active' AND \
               media.update_date_time > media_content.last_sync_time"];

        FMResultSet *resultingSet = [db executeQuery:sql];

        while ([resultingSet next]) {

            int rowCount = [resultingSet intForColumn:@"C"];
            pendingPhotoDownload += rowCount;
        }
        [resultingSet close];

        NSString *sqlQuery = [NSString stringWithFormat:@"SELECT COUNT(pk_id) C FROM rel_user_product"];
        FMResultSet *resultsSets = [db executeQuery:sqlQuery];


        while([resultsSets next]) {

            int rowCount = [resultsSets intForColumn:@"C"];
            pendingDataDownload += rowCount;
        }
        [resultsSets close];
    }];

    double ratio = 0.0;
    if(totalRow)ratio = (double)pendingRow / (double)totalRow * 100.0;

    ratio = floor(100.0 - ratio);
    double downloadRation = 0.0;
    if(pendingDataDownload) downloadRation = 100.0;

    isCalculating = NO;

    NSMutableDictionary* syncStatusDic = [[NSMutableDictionary alloc] init];
    [syncStatusDic setObject:[NSNumber numberWithInt:totalRow] forKey:TOTAL_ROW];
    [syncStatusDic setObject:[NSNumber numberWithInt:pendingRow] forKey:PENDING_ROW];
    [syncStatusDic setObject:[NSNumber numberWithInt:pendingPhotoRow] forKey:PENDING_PHOTO];
    [syncStatusDic setObject:[NSNumber numberWithInt:pendingDataDownload] forKey:PENDING_DATA_DOWNLOAD];
    [syncStatusDic setObject:[NSNumber numberWithInt:pendingPhotoDownload] forKey:PENDING_PHOTO_DOWNLOAD];

    [self.delegate updateSyncStatusValues: [NSDictionary dictionaryWithDictionary:syncStatusDic]]; // safe conversion from NSMutableDictionary to NSDictionary
}

@end

Aucun commentaire:

Enregistrer un commentaire