可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I am doing some database operations in IOS. Basically I want to do this in a background thread. I tried using GCD. But the issue for me is I want to get some values from this process after it is finished. Say before inserting an item to database I check whether the item already exists. Please see code below
__block Boolean isExisting = false;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
NSString *path = [SqliteManager initDatabase];
if(sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{
NSString *query = [NSString stringWithFormat:@"select count (campaignId) from POTC where Id='%@' and taskid='%@' and pocId='%@' and userId='%@'",[submission.campaignId stringRepresentation],[submission.taskId stringRepresentation],[submission.pocId stringRepresentation],[[UUID UUIDWithString:submission.userId] stringRepresentation]];
const char *sql = [query cStringUsingEncoding:NSASCIIStringEncoding];
sqlite3_stmt *selectStatement;
if (sqlite3_prepare_v2(database, sql, -1, &selectStatement, NULL) == SQLITE_OK)
{
while (sqlite3_step(selectStatement) == SQLITE_ROW)
{
if (sqlite3_column_int(selectStatement, 0) >0)
{
isExisting = true;
break;
}
}
sqlite3_finalize(selectStatement);
}
sqlite3_close(database);
}
return isExisting;
});
But the above code with return statement wont work as dispatch-async is expecting a void code block. How can i achieve the same in IOS? Is there something similar to animation completion block in IOS?
回答1:
The block has to have a return type of void because there is nowhere to return the value to in an asynchronous block.
The variable isExisting is qualified __block
which means it will be set whenever the block assigns to it. Unfortunately, your main thread won't have access to it once it has exited the scope. The wary to do this is for your block to call another method (or function, or block) that sets a variable or property that you know will still be around when the asynchronous block has finished.
e.g. you could have a method on the app delegate to invoke on completion.
// in your appDelegate implementation
-(void) updateUIAfterDatabaseUpdate: (bool) isExisting
{
if (isExisting)
{
// e.g. display an error
}
else
{
// successful update
}
}
// The update code
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
bool isExisting = false;
NSString *path = [SqliteManager initDatabase];
if(sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{
// Snipped for clarity
}
dispatch_async(dispatch_get_main_queue(), ^(void) {
[appDelegate updateUIAfterDatabaseUpdate: isExisting] ;
});
});
The dispatch on the main queue ensures that the method is called in the main thread so it can do UI updates.
回答2:
Maybe you should create a function with a completion block.
I define mines like this :
typedef void (^myBlock)(type1 param1,
type2 param2);
-(void)myAsyncFunctionWithParam:(id)paramSend withCompletionBlock:(myBlock)returningBlock
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//here everything you want to do
// especially defining 2 parameters to return (let them be p1 & p2)
dispatch_async(dispatch_get_main_queue(), ^{
returningBlock(p1,p2);
});
});
}
and you can use it like :
[self myAsyncFunctionWithParam:ps
withCompletionBlock:^(type1 paramToUse1,
type2 paramToUse2)
{
//You can use here paramToUse1 and paramToUse2
}
];
You can use whatever type you want for type in the block : NSString, NSDate,... (don't forgive * if needed)
回答3:
You don't have to return something because isExisting will become true and if you access its value after completion of block execution, it returns true.
回答4:
The way you are thinking loses the benefit of the background thread :). You should restructure your program to fit better with the asynchronous paradigm. You launch your background work asynchronously and then when it's finished then you can send a message to a receiver to do another work. In your code for example you can use notification or a simple message send. like this :
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
NSString *path = [SqliteManager initDatabase];
if(sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{
NSString *query = [NSString stringWithFormat:@"select count (campaignId) from POTC where Id='%@' and taskid='%@' and pocId='%@' and userId='%@'",[submission.campaignId stringRepresentation],[submission.taskId stringRepresentation],[submission.pocId stringRepresentation],[[UUID UUIDWithString:submission.userId] stringRepresentation]];
const char *sql = [query cStringUsingEncoding:NSASCIIStringEncoding];
sqlite3_stmt *selectStatement;
if (sqlite3_prepare_v2(database, sql, -1, &selectStatement, NULL) == SQLITE_OK)
{
while (sqlite3_step(selectStatement) == SQLITE_ROW)
{
if (sqlite3_column_int(selectStatement, 0) >0)
{
// Here you can send the notification with the data you want.
break;
}
}
sqlite3_finalize(selectStatement);
}
sqlite3_close(database);
}
return isExisting;
});