iOS:Method不会等待它调用的其他方法完成

时间:2013-04-09 19:18:57

标签: ios objective-c database

我在我的一个iPhone应用程序中使用此模型:

//
//  CatapultAccount.m
//  Catapult
//
//  Created by Aziz Light on 4/9/13.
//  Copyright (c) 2013 Catapult Technology Ltd. All rights reserved.
//

#import "CatapultAccount.h"

@interface CatapultAccount ()

- (BOOL)createAccountsTable;

- (BOOL)accountWithNameIsAlreadyAdded:(NSString *)accountName;

- (NSDictionary *)getAccountLogosForClient:(NSDictionary *)client;

- (NSString *)getAccountLogoFromURL:(NSURL *)url;

- (NSString *)saveImage:(UIImage *)image
           withFileName:(NSString *)imageName
                 ofType:(NSString *)extension
            inDirectory:(NSString *)directoryPath;

@end

@implementation CatapultAccount

- (BOOL)createAccountWithAccountID:(NSString *)accountID
{
    __block BOOL operationSuccessfull;

    NXOAuth2Account *account = [[NXOAuth2AccountStore sharedStore] accountWithIdentifier:accountID];

    if (account == nil) {
        operationSuccessfull = NO;
    } else {
        if ([self openDatabaseConnection]) {
            if ([self createAccountsTable]) {

                [NXOAuth2Request performMethod:@"GET"
                                    onResource:[NSURL URLWithString:[NSString stringWithFormat:@"%@/users/me", kCatapultHost]]
                               usingParameters:nil
                                   withAccount:account
                           sendProgressHandler:nil
                               responseHandler:^(NSURLResponse *response, NSData *responseData, NSError *error) {
                                   if (error != nil) {
                                       operationSuccessfull = NO;
#if DEBUG
                                       NSLog(@"ERROR: %@", error);
#endif
                                       _lastError = error;
                                   } else {
                                       NSError *jsonError;

                                       NSDictionary *serializedResponse = [NSJSONSerialization JSONObjectWithData:responseData
                                                                                                          options:kNilOptions
                                                                                                            error:&jsonError];

                                       if (jsonError != nil) {
                                           operationSuccessfull = NO;
#if DEBUG
                                           NSLog(@"ERROR: %@", jsonError);
#endif
                                           _lastError = jsonError;
                                       } else {
                                           NSDictionary *user   = [serializedResponse objectForKey:@"user"];
                                           NSDictionary *client = [serializedResponse objectForKey:@"client"];

                                           NSString *forename    = [user objectForKey:@"forename"];
                                           NSString *surname     = [user objectForKey:@"surname"];
                                           NSString *accountName = [client objectForKey:@"account_name"];
                                           NSString *clientName  = [client objectForKey:@"client_name"];

                                           if ([self accountWithNameIsAlreadyAdded:accountName]) {
                                               operationSuccessfull = NO;

                                               _lastError = [NSError errorWithDomain:kCatapultAccountErrorDomain
                                                                                code:kCatapultDuplicateAccountErrorCode
                                                                            userInfo:@{@"message": @"You have already added this account"}];

#if DEBUG
                                               NSLog(@"ERROR: %@", _lastError);
#endif
                                           } else {
                                               NSDictionary *logos = [self getAccountLogosForClient:client];

                                               operationSuccessfull = [self.db executeUpdate:@"insert into accounts(account_id, forename, surname, account_name, client_name, smallest_logo, thumb_logo) values(?,?,?,?,?,?,?)",
                                                                       accountID, forename, surname, accountName, clientName, logos[@"smallest_logo"], logos[@"thumb_logo"]];
                                           }
                                       }
                                   }
                               }];

            } else {
                operationSuccessfull = NO;

                _lastError = [NSError errorWithDomain:kCatapultDatabaseErrorDomain
                                                 code:kCatapultUnableToCreateTableErrorCode
                                             userInfo:@{@"message": @"Unable to create the accounts table"}];

#if DEBUG
                NSLog(@"ERROR: %@", _lastError);
#endif
            }

            [self closeDatabaseConnection];
        } else {
            operationSuccessfull = NO;

            _lastError = [NSError errorWithDomain:kCatapultDatabaseErrorDomain
                                             code:kCatapultUnableToOpenDatabaseConnectionErrorCode
                                         userInfo:@{@"message": @"Unable to open database connection"}];

#if DEBUG
            NSLog(@"ERROR: %@", _lastError);
#endif
        }
    }

    return operationSuccessfull;
}

- (BOOL)createAccountsTable
{
    // Accounts table schema
    // id integer primary key autoincrement
    // account_id varchar(36) not null - unique
    // forename varchar(255) not null
    // surname varchar(255) not null
    // account_name varchar(255) not null - unique
    // client_name varchar(255) not null
    // smallest_account_logo text
    // thumb_account_logo text

    BOOL tableCreationWasSuccessfull = [self.db executeUpdate:@"create table if not exists accounts(id integer primary key autoincrement, account_id varchar(36) not null, forename varchar(255) not null, surname varchar(255) not null, account_name varchar(255) not null, client_name varchar(255) not null, smallest_logo text, thumb_logo text, unique(account_id, account_name) on conflict abort)"];

    if (tableCreationWasSuccessfull) {
        _lastError = nil;
    } else {
        _lastError = [self.db lastError];
#if DEBUG
        NSLog(@"Failed to create users table: %@", _lastError);
#endif
    }

    return tableCreationWasSuccessfull;
}

- (BOOL)accountWithNameIsAlreadyAdded:(NSString *)accountName
{
    FMResultSet *account = [self.db executeQuery:@"select count(*) from accounts"];
    return [account next];
}

- (NSDictionary *)getAccountLogosForClient:(NSDictionary *)client
{
    NSString *smallestLogoURLString = [[[client objectForKey:@"logo"] objectForKey:@"smallest"] objectForKey:@"url"];
    NSString *smallestLogoPath = [self getAccountLogoFromURL:[NSURL URLWithString:smallestLogoURLString]];

    NSString *thumbLogoURLString = [[[client objectForKey:@"logo"] objectForKey:@"thumb"] objectForKey:@"url"];
    NSString *thumbLogoPath = [self getAccountLogoFromURL:[NSURL URLWithString:thumbLogoURLString]];

    return @{@"smallest_logo": smallestLogoPath, @"thumb_logo": thumbLogoPath};
}

- (NSString *)getAccountLogoFromURL:(NSURL *)url
{
    NSString *urlWithoutGETParams = [[[url absoluteString] componentsSeparatedByString:@"?"] objectAtIndex:0];
    NSString *lastSegmentOfURL = [[urlWithoutGETParams componentsSeparatedByString:@"/"] lastObject];
    NSString *logoName = [[lastSegmentOfURL componentsSeparatedByString:@"."] objectAtIndex:0];
    NSString *logoExtension =  [[lastSegmentOfURL componentsSeparatedByString:@"."] lastObject];

    NSString * documentsDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];

    NSString *logoPath = [documentsDirectoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", logoName, logoExtension]];

    BOOL logoExists = [[NSFileManager defaultManager] fileExistsAtPath:logoPath];

    if (logoExists) {
        return logoPath;
    } else {
        NSData *data = [NSData dataWithContentsOfURL:url];

        UIImage *logo = [UIImage imageWithData:data];

        logoPath = [self saveImage:logo withFileName:logoName ofType:logoExtension inDirectory:documentsDirectoryPath];

        return (logoPath == nil) ? nil : logoPath;
    }
}

- (NSString *)saveImage:(UIImage *)image
           withFileName:(NSString *)imageName
                 ofType:(NSString *)extension
            inDirectory:(NSString *)directoryPath
{
    NSData *imageRepresentation;

    if ([[extension lowercaseString] isEqualToString:@"png"] || [[extension lowercaseString] isEqualToString:@"gif"]) {
        imageRepresentation = UIImagePNGRepresentation(image);
    } else if ([[extension lowercaseString] isEqualToString:@"jpg"] || [[extension lowercaseString] isEqualToString:@"jpeg"]) {
        imageRepresentation = UIImageJPEGRepresentation(image, 1.0);
    } else {
#if DEBUG
        NSLog(@"Image Save Failed\nExtension: (%@) is not recognized, use (PNG/JPG/GIF)", extension);
#endif
        return nil;
    }

    NSString *imagePath = [directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", imageName, extension]];

    NSError *error;
    BOOL imageDidSave = [imageRepresentation writeToFile:imagePath
                                                 options:NSAtomicWrite
                                                   error:&error];

    if (error != nil) {
#if DEBUG
        NSLog(@"Error saving the file: %@", error);
#endif
    }

    return (imageDidSave) ? imagePath : nil;
}

@end

在我的一个视图控制器中使用此方法:

- (void)createAccount:(NSNotification *)notification
{
    NXOAuth2Account *account = [notification.userInfo objectForKey:@"NXOAuth2AccountStoreNewAccountUserInfoKey"];

    if ([_accountModel createAccountWithAccountID:account.identifier]) {
        // Do something
        NSLog(@"Yay");
    } else {
        // Delete the newly created account
        [[NXOAuth2AccountStore sharedStore] removeAccount:account];

        UIAlertView *errorMessage = [[UIAlertView alloc] initWithTitle:@"Account Error"
                                                               message:@"Unable to add new account"
                                                              delegate:nil
                                                     cancelButtonTitle:@"OK"
                                                     otherButtonTitles:nil];

        [errorMessage show];
    }

    [self dismissViewControllerAnimated:YES completion:nil];
}

问题在于,遵循我的(天真)观察,大多数在模型中使用数据库的方法执行得太慢,createAccountWithAccountID:方法不等待它调用的方法完成。结果是记录没有保存到数据库,但由于某种原因,createAccountWithAccountID方法返回YES ...这里是说明我所说的日志:

2013-04-09 20:07:46.261 Catapult[21004:c07] Yay
2013-04-09 20:07:46.276 Catapult[21004:c07] The FMDatabase <FMDatabase: 0x7288300> is not open.
2013-04-09 20:07:46.606 Catapult[21004:c07] The FMDatabase <FMDatabase: 0x7288300> is not open.

记录未保存到数据库,因为数据库连接关闭得太快......

有人知道如何解决我的问题吗?

1 个答案:

答案 0 :(得分:1)

大多数使用完成块的方法都是异步执行的。该方法将立即返回,并在请求实际完成时执行该块。您需要在代码中相应地处理它。发送请求后不要释放任何内容,而是根据响应处理程序块中的结果执行任何操作,然后释放它。