I've created this method in my iOS to call to FMDatabaseQueue:
-(void) enqueueSelectStatement:(NSString*)selectStatement withArguments:(NSArray*)args {
NSLog(#"Checkpoint 1");
[self.dbQueue inDatabase:^(FMDatabase *db) {
FMResultSet *result = [db executeQuery:selectStatement withArgumentsInArray:args];
NSLog(#"Checkpoint 2");
}];
NSLog(#"Checkpoint 3");
}
When I call this method with:
NSString *query = #"SELECT * FROM mytable WHERE id = ?";
[self enqueueSelectStatement:query withArguments:#[1]];
I get Checkpoint 1 in my terminal output, but nothing else. The app does not crash and continues to run, but the method/block apparently dies after Checkpoint 1
Is the method just stopping, and if so, why?
Related
I have this code:
- (NSString *) login {
datos=#"";
NSString __block *variable;
NSString *sqlQueryExisteUsuario;
sqlQueryExisteUsuario = [[NSString alloc] initWithFormat:#"SELECT COUNT(*) FROM tableName WHERE field='value' AND field='value'"];
SQLClient* client = [SQLClient sharedInstance];
client.delegate = self;
[client connect:#"serverName" username:#"username" password:#"password" database:#"database" completion:^(BOOL success) {
[client execute:sqlQueryExisteUsuario completion:^(NSArray* results) {
variable = [self processLogin:results];
NSLog(#"In: %#",variable);
[client disconnect];
}];
}];
NSLog(#"Out: %#",variable);
return nil;
}
- (NSString *)processLogin:(NSArray*)data
{
existeArray = [NSMutableArray array];
for (NSArray* table in data)
for (NSDictionary* row in table)
for (NSString* column in row)
[existeArray addObject:row[column]];
NSString *existe=existeArray[0];
if([existe isEqualToString:#"1"])
{
datos=#"yes";
}else{
datos=#"no";
}
return datos;
}
In the first call to NSLog, which begins with In, the value shows. In the second call, which begins with Out, the value doesn't show. Why?
Your connect is async method, so NSLog... line will be executed earlier than completion block. So, you have to use blocks also:
- (NSString *) loginWithCompletion:(void(^)(NSString *result))handler
{
datos=#"";
NSString *sqlQueryExisteUsuario;
sqlQueryExisteUsuario = [[NSString alloc] initWithFormat:#"SELECT COUNT(*) FROM tableName WHERE field='value' AND field='value'"];
SQLClient* client = [SQLClient sharedInstance];
client.delegate = self;
[client connect:#"serverName" username:#"username" password:#"password" database:#"database" completion:^(BOOL success) {
if (success) {
[client execute:sqlQueryExisteUsuario completion:^(NSArray* results) {
NSString *variable = [self processLogin:results];
NSLog(#"In: %#",variable);
[client disconnect];
if (handler) {
handler (variable);
}
}];
} else {
//TODO: handle this
if (handler) {
handler (nil);
}
}
}];
}
Usage:
- (void)ff
{
[self loginWithCompletion:^(NSString *variable) {
//Do something with variable
}];
}
The problem is that your variable is being set inside a completion block: the variable variable (not a great name, BTW!) is set inside not only one but two blocks - that's the "completion" part of your code – both of which are best thought of a bit(!) like a miniature anonymous function: in this case, they get run when the system is ready for it, not straight away.
iOS will start execution of your connect code, then jump ahead to NSLog(#"Out: %#",variable), then execute the completion block of connect, which in turn starts more code (execute), which has its own completion block, which finally gets executed. As #rmaddy says in a comment below, the technical name for this is asynchronous code: the bit inside your block does not get executed immediately, which allows the system to continue doing other things while waiting for your task to complete.
So the running order will be:
1) You declare variable.
2) Your connection code starts.
3) variable gets printed out.
4) The connection completes.
5) Your execute code starts.
6) Your execute code completes.
7) variable gets set to the final value.
I'm trying to develop and Action Extension for iOS9.1 that is supposed to query Parse for some data.
I've added, enabled and tested Parse in the extension and I'm successful at creating test objects and checking for current user.
I can not get the code inside Parse's query method
[query findObjectsInBackgroundWithBlock:^ to execute. LLDB just keeps skipping it so I'm really at a loss.
This code executes perfectly within the container app so I'm a bit confused.
- (void)viewDidLoad
{
[super viewDidLoad];
[Parse enableLocalDatastore];
[Parse enableDataSharingWithApplicationGroupIdentifier:#"group.com.app.slinky"
containingApplication:#"com.app.Slinky"];
[Parse setApplicationId:#"xxxxx"
clientKey:#"xxxxx"];
for (NSExtensionItem *item in self.extensionContext.inputItems) {
for (NSItemProvider *itemProvider in item.attachments) {
if ([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypePropertyList]) {
[itemProvider loadItemForTypeIdentifier:(NSString *)kUTTypePropertyList options:nil completionHandler:^(NSDictionary *jsDict, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
NSDictionary *jsPreprocessingResults = jsDict[NSExtensionJavaScriptPreprocessingResultsKey];
NSString *pageTitle = jsPreprocessingResults[#"title"];
NSString *pageURL = jsPreprocessingResults[#"URL"];
if ([pageURL length] > 0) {
self.siteURL.text = pageURL;
self.URLstring = pageURL;
}
if ([pageTitle length] > 0) {
self.siteTitle.text = pageTitle;
}
});
}];
break;
}
}
}
[self queryParse];
}
-(void)queryParse{
PFQuery *query = [self.friendsRelation query];
[query orderByAscending:#"username"];
**[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
NSLog(#"%# %#", error, [error userInfo]);
} else {
self.friends = objects;
[self.tableView reloadData];
}
}];**
}
There are some limit to fetch Objects from Parse. Read Here
One has a limit of obtaining 100 objects, but anything from 1 to 1000 has a valid limit.
You have to set the limit again for the query for next objects and skip the properties of PFQuery.
You will have to query again and again until you reach the total count.
For Example - If you have 3000 objects to be fetched, then you have to query 3 times with a count of say 1000.
OR
Call [self queryParse]; inside dispatch_async(dispatch_get_main_queue(), ^{ }); block.
Hope this might workout.
This was just a result of a 12 hour refractoring marathon :-(
When I moved the queryParse call into its own method. I lost the definition of
self.friendsRelation = [[PFUser currentUser] objectForKey:#"friendsRelation"];
essentially sending a bad query... I don't know why it isn't return an error but regardless I can check that off now.
Thank you all for you help!
I have this code that is trying to do a background fetch for HealthKit data. The code works fine when I first run the app, but if I manually perform a background fetch (using the debug command), I get an exception thrown and an error that says reason: 'this request has been neutered - you can't call -sendResponse: twice nor after encoding it' and I'm not quite sure why.
Here is the code that fetches the data:
- (void)fetchNewDataWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
if (!self.healthStore) {
self.healthStore = [HKHealthStore new];
}
dataTypes = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:1], HKQuantityTypeIdentifierStepCount,
[NSNumber numberWithInt:2], HKQuantityTypeIdentifierFlightsClimbed,
[NSNumber numberWithInt:3], HKQuantityTypeIdentifierDistanceWalkingRunning,
[NSNumber numberWithInt:4], HKQuantityTypeIdentifierDistanceCycling, nil];
achievementData = [NSMutableDictionary new];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *startDate = [calendar startOfDayForDate:[NSDate date]];
NSDate *endDate = [calendar dateByAddingUnit:NSCalendarUnitDay value:1 toDate:startDate options:0];
NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionNone];
for (NSString *key in dataTypes) {
HKSampleType *sampleType = [HKSampleType quantityTypeForIdentifier:key];
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:sampleType predicate:predicate limit:HKObjectQueryNoLimit sortDescriptors:nil resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
if (!error) {
if (!results) {
NSLog(#"No results were returned form query");
completionHandler(UIBackgroundFetchResultNoData);
} else {
dispatch_async(dispatch_get_main_queue(), ^{
[self processNewDataWithResults:results andType:key];
completionHandler(UIBackgroundFetchResultNewData);
});
}
} else {
NSLog(#"Error: %# %#", error, [error userInfo]);
completionHandler(UIBackgroundFetchResultFailed);
}
}];
[self.healthStore executeQuery:query];
}
}
Then I have a separate function that processes the data that you can see gets called when results are found. If you want I can paste it in here but it is pretty lengthy, not sure if it has anything to do with it.
I have tried putting breakpoints in to see when the completion handler is called but from what I can tell it is only getting called once, unless I am missing something silly here.
If anyone has any advice, please let me know :) Thanks!
EDIT
Here is what the error message looks like:
2015-05-13 10:11:54.467 appName[379:169163] *** Assertion failure in -[UIFetchContentInBackgroundAction sendResponse:], /SourceCache/BaseBoard/BaseBoard-98.3/BaseBoard/BSAction.m:221
2015-05-13 10:11:54.470 appName[379:169163] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'this request has been neutered - you can't call -sendResponse: twice nor after encoding it'
*** First throw call stack:
(0x28ed4137 0x36e30c77 0x28ed400d 0x29bd2bc9 0x2dbde865 0x397ed5 0x2dbde7cf 0x2ca82a3d 0x39019b 0x390187 0x393e9d 0x28e99889 0x28e97fa9 0x28de39a1 0x28de37b3 0x305951a9 0x2c56e695 0xdff29 0x373d8aaf)
libc++abi.dylib: terminating with uncaught exception of type NSException
From looking at the code you posted, I do not see any other way than that the background fetch completion handler will be called exactly 4 times due to the for loop.
The code paths in the resultHandlers of each instance of HKSampleQuery will end up at a completionHandler(UIBackgroundFetchResult...) call in any case and you are always instantiating four of them, so this is what the assertion 'you can't call -sendResponse: twice' is complaining about IMO.
It should be easy to check if that is the problem by commenting out 3 of the queries from the dataTypes dictionary.
Edit: as requested in the comments, here's a possible solution (..just off the top of my head, so take it with a grain of salt..):
It (a) uses a semaphore lock to turn the asynchronous query result callback into a "synchronous" call & (b) uses a dispatch group to wait for all 4 queries to finish before executing the completion handler.
// NOTE: This example assumes that the fetch has "new data" if any of the queries returned something
// Also it skips the 'NSError' part (which could work exactly like the 'result' var)
// Define one or more block-global result variables for queries can put their end state into
UIBackgroundFetchResult __block result = UIBackgroundFetchResultNoData;
// Create a dispatch group that will manage all the concurrent queries
dispatch_queue_t queue = dispatch_queue_create([#"my.query.queue" UTF8String], DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t queries = dispatch_group_create();
// For each dataType dispatch a group block containing the query to run on the concurrent queue
for (NSString *key in dataTypes) {
dispatch_group_async(queries, queue, ^{
// To work around the asynchronous callback, I'll use a lock to wait for the query to return their result, so..
// ..like above, a block-global var will hold the result of the query
BOOL __block success = NO;
// ..create a one-time lock..
dispatch_semaphore_t lock = dispatch_semaphore_create(0);
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:sampleType predicate:predicate limit:HKObjectQueryNoLimit sortDescriptors:nil resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
success = YES; // ..or however you define success.. ;)
dispatch_semaphore_signal(lock); // ..open lock to signal result available..
}];
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); // ..wait for callback result lock to open..
// ..determine & set result.
if (success) {
result = UIBackgroundFetchResultNewData;
}
});
}
// Schedule a final block to execute (on the main queue) when all the other group blocks have finished running
dispatch_group_notify(queries, dispatch_get_main_queue(), ^{
// Determine final result and call completion handler
completionHandler(result);
});
I am using a multi threaded environment in my app, I need to constantly access sqlite db in order to update my views and also update my DB with server data via multiple background threads. Right now I am using FMDB for DB interaction but still getting DB locked problem.
FMDatabaseQueue *_queue = [FMDatabaseQueue databaseQueueWithPath:databasePath];
NSOperationQueue *_writeQueue = [NSOperationQueue new];
[_writeQueue setMaxConcurrentOperationCount:1];
NSRecursiveLock *_writeQueueLock = [NSRecursiveLock new];
[_writeQueue addOperationWithBlock:^{
BOOL tryLock = NO;
#try {
[_writeQueueLock lock];
tryLock = YES;
[_queue inDatabase:^(FMDatabase *db) {
#try {
[db logsErrors];
[db executeUpdate:updateSQL];
}
#catch (NSException *exception) {
}
#finally {
}
}];
}
#catch (NSException *exception) {
NSLog(#"Error while inserting data saveLocation inside operation queue. %#", exception.description);
}
#finally {
if (tryLock) {
[_writeQueueLock unlock];
}
}
}];
This what I am doing every time I insert data and similar way when I read data from DB as I am locking, Process should not be able to access DB until one thread finishes. I don't know what is wrong please help me out.
Whenever multiple threads try to access same table to read and write or two threads wants to write on same table of same db sqlite produces db locked signal so to resolve this you need Locks
NSRecursiveLock *_writeQueueLock = [NSRecursiveLock new];
as you've added in your code, but this won't help you much as you're trying to craete a new lock every time you insert.
This lock should be a single object for all your blocking calls to DB like insert, update, delete etc.
Try creating a singleton instance of lock, This should help:
static FMDatabaseQueue *_queue;
static NSOperationQueue *_writeQueue;
static NSRecursiveLock *_writeQueueLock;
+(SomeDBClass*)getSharedInstance{
if (!sharedInstance) {
sharedInstance = [[super allocWithZone:NULL]init];
_queue = [FMDatabaseQueue databaseQueueWithPath:databasePath];
_writeQueue = [NSOperationQueue new];
[_writeQueue setMaxConcurrentOperationCount:1];
_writeQueueLock = [NSRecursiveLock new];
}
return sharedInstance;
}
Now once your objects created you can call your insert, update, delete method on these queue and locks like:
[_writeQueue addOperationWithBlock:^{
BOOL tryLock = NO;
#try {
[_writeQueueLock lock];
tryLock = YES;
[_queue inDatabase:^(FMDatabase *db) {
#try {
[db logsErrors];
[db executeUpdate:updateSQL];
}
#catch (NSException *exception) {
}
#finally {
}
}];
}
#catch (NSException *exception) {
NSLog(#"Error while inserting data saveLocation inside operation queue. %#", exception.description);
}
#finally {
if (tryLock) {
[_writeQueueLock unlock];
}
}
}];
You can also refer this for better understanding, Hope this helps and I am new to the stack so please be little bit forgiving Thanks.
Using FMDatabaseQueue completely eliminates the need for any other locking mechanism. Adding another locking mechanism is only going to complicate the issue further.
If you are getting messages about the database being "locked", it because either:
you have multiple FMDatabase/FMDatabaseQueue objects out there; or
you're calling inDatabase from within another inDatabase call.
You should have one FMDatabaseQueue object which is shared amongst all of the threads and you need to make sure that none of your functions called with a inDatabase block calls something else that itself tries another inDatabase call.
I am developing an app for ipad and i am using sqlite sentences (select, update, insert, delete).
I open (sqlite3_open) the database at the beginning and close (sqlite3_close) at the end of each sentence. But sometimes i´ve got the "database is locked" message.
I don´t know what can i do to solve this.
Thanks and sorry for this little information.
If I'm not mistaken , the problem with sqllite is that you can only access it once at a time.
If you have multiple threads, you can run in this situation. Example:
Run method1 (which accesses the database) on thread t1.
Run method2 (which accesses the database) on thread t2 after x seconds.
If method1 is not finished in those x seconds , both methods will access it at the same time.
And , as I said , I know that sqllite does not support this.
You should try to flag the usage of your database and if you want to access it but it is in use , try again after x seconds. Like this:
- (void) generalMethodThatUsesDatabses
{
if(databaseIsUsed)
{
[self performSelector:#selector(generalMethodThatUsesDatabses) withObject:nil afterDelay:5];
return;
}
databaseIsUsed = TRUE; //global bool variable
//your code here
databaseIsUsed = FALSE;
}
Hope this helps. Cheers!
You probably opened the database before using the same simulator.
To conclude all actions to a database and release all resources you always
have to use both (!) statements:
sqlite3_finalize(statement);
sqlite3_close(database);
A good way to solve this is to wrap this into a C++ library. This way, you can can create the library wrapper on the stack. This means that the moment that the function goes out of scope, you can close the connection in the destructor.
(note that I use reference counting for the Objective-C)
For instance:
NSArray* ScoreBoard::getAllScores()
{
ScoreBoard::ensureExistingTable();
//Stack allocated
SqliteWrapper sqlite("Scores.sqlite");
NSArray* result = sqlite.RunQuery("SELECT * FROM Scores ORDER BY ID DESC");
return result;
//after this, the sqlite destructor is called
}
It is very nice that the Objective-C compiler allows you to merge C++. It can be extremely useful.
Also
void SqliteWrapper::Close()
{
sqlite3_close(db);
}
as Vincent has pointed out, you have to finalize the statement. If you want keep the connection open, use finalize after each statement. Close out the connection the moment you are discarding the connection.
This method works for me.
it is use for three methed
1.isert
2.update
3. delete.
-(NSMutableArray *)resultSet
-(void)insertWithTitle:(NSString *)title Body:(NSString *)body
-(BOOL)updateAtIndex:(int)index Title:(NSString *)title Body:(NSString *)body
NSMutableArray *result = [[[NSMutableArray alloc] initWithCapacity:0] autorelease];
FMResultSet *rs = [db executeQuery:[self SQL:#"SELECT * FROM %#" inTable:TABLE_NAME]];
while ([rs next]) {
Record *tr = [[Record alloc] initWithIndex:[rs intForColumn:#"id"]
Title:[rs stringForColumn:#"title"]
Body:[rs stringForColumn:#"body"]];
[result addObject:tr];
[tr release];
}
[rs close];
2....
return result;
[db executeUpdate:[self SQL:#"INSERT INTO %# (title, body) VALUES (?,?)" inTable:TABLE_NAME], title, body];
if ([db hadError]) {
NSLog(#"Err %d: %#", [db lastErrorCode], [db lastErrorMessage]);
Delete record :
BOOL success = YES;
[db executeUpdate:[self SQL:#"DELETE FROM %# WHERE id = ?" inTable:TABLE_NAME], [NSNumber numberWithInt:index]];
if ([db hadError]) {
NSLog(#"Err %d: %#", [db lastErrorCode], [db lastErrorMessage]);
success = NO;
}
return success;
}