我在我的一个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.
记录未保存到数据库,因为数据库连接关闭得太快......
有人知道如何解决我的问题吗?
答案 0 :(得分:1)
大多数使用完成块的方法都是异步执行的。该方法将立即返回,并在请求实际完成时执行该块。您需要在代码中相应地处理它。发送请求后不要释放任何内容,而是根据响应处理程序块中的结果执行任何操作,然后释放它。