Database locked using FMDB for iOS - ios

First of all, I'd like to say that I'm really having a bad time trying to configure and use my SQLite DB in a background thread so that the main thread is not blocked.
After I found a little guide somewhere on the Internet, I've decided to go for the FMDB wrapper.
All the methods related to the DB operations are in the same class and this is where I'm getting errors:
I've set the static variables like this:
static FMDatabaseQueue *_queue;
static NSOperationQueue *_writeQueue;
static NSRecursiveLock *_writeQueueLock;
Then in my init method I have:
- (id)init {
self = [super init];
if (self) {
_queue = [FMDatabaseQueue databaseQueueWithPath:[self GetDocumentPath]];
_writeQueue = [NSOperationQueue new];
[_writeQueue setMaxConcurrentOperationCount:1];
_writeQueueLock = [NSRecursiveLock new];
}
return self;
}
And this is the method that gives me the error:
- (void)UpdateTime:(NSString *)idT :(int)userId {
[_writeQueue addOperationWithBlock:^{
[_writeQueueLock lock];
[_queue inDatabase:^(FMDatabase *dbase) {
AppDelegate *deleg = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if (![dbase executeUpdate:#"update orari set orario=datetime(orario, '? minutes') where nome=? and dataid>=? and idutente=?"
withArgumentsInArray:#[[NSNumber numberWithFloat:deleg.diff], deleg.nome, [NSNumber numberWithInt:deleg.idMed], [NSNumber numberWithInt: userId]]]) {
NSLog(#"error");
}
}];
[_writeQueueLock unlock];
}];
[_writeQueue addOperationWithBlock:^{
[_writeQueueLock lock];
[_queue inDatabase:^(FMDatabase *dbase) {
AppDelegate *deleg = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if (![dbase executeUpdate:#"UPDATE orari SET presa=1 where dataid=? and idutente=?"
withArgumentsInArray:#[[NSNumber numberWithInt:deleg.identific], [NSNumber numberWithInt: userId]]]) {
NSLog(#"error");
}
}];
[_writeQueueLock unlock];
}];
[self AddNotification];
}
These are the errors I'm getting:
*** -[NSRecursiveLock dealloc]: lock (<NSRecursiveLock: 0xc38b350> '(null)') deallocated while still in use
DB Error: 5 "database is locked"
*** -[NSRecursiveLock unlock]: lock (<NSRecursiveLock: 0x13378d20> '(null)') unlocked when not locked
From the guide I've read, I supposed that the access to my DB would have been "serialized", and each update would have been added to a queue and executed one at a time.
As you can see, I have a lot to learn about this topic, so any help would really be appreciated.

As I can See you have not created shared instance or singleton instance of this init call
- (id)init {
self = [super init];
if (self) {
_queue = [FMDatabaseQueue databaseQueueWithPath:[self GetDocumentPath]];
_writeQueue = [NSOperationQueue new];
[_writeQueue setMaxConcurrentOperationCount:1];
_writeQueueLock = [NSRecursiveLock new];
}
return self;
}
This should be a singleton call as you will create multiple instance of NSOperationQueue which will make DB vulnurable in a multi-threaded environment, try making it singleton call for your database either using GCD or
static DBManager *sharedInstance = nil;
+(DBManager*)getSharedInstance{
if (!sharedInstance) {
sharedInstance = [[super allocWithZone:NULL]init];
_queue = [FMDatabaseQueue databaseQueueWithPath:[self GetDocumentPath]];
_writeQueue = [NSOperationQueue new];
[_writeQueue setMaxConcurrentOperationCount:1];
_writeQueueLock = [NSRecursiveLock new];
}
return sharedInstance;
}
It might solve your problem and this is first time I am answering here and I am new to the environment so please be little bit forgiving :) Thanks

Related

NSOperationQueue ....unrecognized selector sent

I am trying to implement a contact app.
In the MyContactsViewController, I am trying to get access to Contacts and if the access is granted I will fetch the contacts from my address book. The ContactHandler is my model class(singleton) which has the method called getAllContacts to get the contacts in a NSMutableArray.
- (void)viewDidLoad {
[super viewDidLoad];
contactHandler = [ContactHandler sharedInstance];
if(!self.accessGranted){
NSOperationQueue *queue =[[ NSOperationQueue alloc]init];
[queue performSelectorOnMainThread:#selector(getAccessToAddressBook) withObject:self waitUntilDone:YES];
contactList = [contactHandler getAllContacts];
}
else{
contactList = [contactHandler getAllContacts];
}
}
-(BOOL)getAccessToAddressBook{
CNContactStore * contactStore = [[CNContactStore alloc] init];
if( [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts] == CNAuthorizationStatusNotDetermined){
[contactStore requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
if(granted){
self.accessGranted = YES;
}
else{
self.accessGranted = NO;
}
}];
}
else if( [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts]== CNAuthorizationStatusAuthorized){
self.accessGranted = YES;
}
else{
self.accessGranted = NO;
}
return self.accessGranted;
}
But I am getting this error -
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSOperationQueue getAccessToAddressBook]: unrecognized selector sent to instance 0x137630700'
Can anyone please help.
Your problem is this line:
[queue performSelectorOnMainThread:#selector(getAccessToAddressBook) withObject:self waitUntilDone:YES];
You're asking queue to perform getAccessToAddressBook when is self who has this selector
If you want to run the method getAccessToAddressBook on the queue, you can use - addOperationWithBlock:
As in this line: performSelectorOnMainThread you are passing withObject:self, where as in method definition i.e (BOOL)getAccessToAddressBookyou are not using it, which is causing the crash, so make withObject:nil , if you don't want to pass any object or value to the method.
Change this line:-
[queue performSelectorOnMainThread:#selector(getAccessToAddressBook) withObject:self waitUntilDone:YES];
to this:-
[queue performSelectorOnMainThread:#selector(getAccessToAddressBook) withObject:nil waitUntilDone:YES];
Or the better approach will be to write like this:-
NSOperationQueue *opQueue=[NSOperationQueue new];
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(getAccessToAddressBook) object:nil];
[opQueue addOperation:op ];

Azure Mobile Services Offline Sync on Existing Project

I have followed this article to the letters on the sample app it works fine but in my existing project same code is not doing what it suppose to do. Here's the code:
ParentService:
-(MSCoreDataStore *)store{
if (_store == nil) {
NSManagedObjectContext * context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
context.parentContext=[[DataBaseDao sharedDao] managedObjectContext];
_store = [[MSCoreDataStore alloc] initWithManagedObjectContext:context];
}
return _store;
}
-(ParentService *) init
{
self = [super init];
if (self) {
// Initialize the Mobile Service client with your URL and key
self.client = [MSClient clientWithApplicationURLString:kApplicationURL
applicationKey:kApplicationKey];
AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = delegate.managedObjectContext;
MSCoreDataStore *store = [[MSCoreDataStore alloc] initWithManagedObjectContext:context];
self.client.syncContext = [[MSSyncContext alloc] initWithDelegate:nil dataSource:store callback:nil];
}
return self;
}
Here is the code for SearchService that inherits Parent Service:
static SearchService *singletonInstance;
+ (SearchService*)getInstance{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singletonInstance = [[super alloc] init];
});
return singletonInstance;
}
-(SearchService *) init
{
self = [super init];
if (self) {
_searchTable = [self.client syncTableWithName:#"Search"];
}
return self;
}
In addItem function call to insert occurs with no errors and auto generated id is given with result dictionary and call to syncData occurs but no updates on the server side. Also unable to retrieve data from local store as well. I am unable to verify that record is added to the store or not because .sqlite asks for password.
I have spent several hours on figuring it out but no luck. Odd thing is I am not getting any error which tells me that I might be missing something what that I need to figure out with your help.

NSOperationQueue: Trouble understanding the order [duplicate]

This question already has answers here:
NSOperationQueue serial FIFO queue
(3 answers)
Closed 9 years ago.
I'm having trouble understanding the way NSOperationQueue works.
Say I have:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount=1;
[queue addOperationWithBlock:^{
[someObject someSelector];
}];
[queue addOperationWithBlock:^{
[someObject anotherSelector];
}];
The second block is being called even before the first block finishes - the opposite of what I want. I tried using – performSelectorOnMainThread:withObject:waitUntilDone: instead, but the second block is still being executed first - presumably because the block thread is not being completed on the main thread, and so it is not blocked with waitUntilDone. I added a break point inside my someSelector block, and it is reached after a break point inside the second block.
I don't quite get it. Help me!!
If there are explicit dependencies between the operations, then use addDependency:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount=1;
NSOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
[someObject someSelector];
}];
NSOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
[someObject anotherSelector];
}];
[operation2 addDependency:operation1];
[queue addOperation:operation1];
[queue addOperation:operation2];
If your operations are doing asynchronous activity, then you should define a custom operation, and only call completeOperation (which will post the isFinished message) when the asynchronous task is done).
// SomeOperation.h
#import <Foundation/Foundation.h>
#interface SomeOperation : NSOperation
#end
and
// SomeOperation.m
#import "SomeOperation.h"
#interface SomeOperation ()
#property (nonatomic, readwrite, getter = isFinished) BOOL finished;
#property (nonatomic, readwrite, getter = isExecuting) BOOL executing;
#end
#implementation SomeOperation
#synthesize finished = _finished;
#synthesize executing = _executing;
#pragma Configure basic operation
- (id)init
{
self = [super init];
if (self) {
_finished = NO;
_executing = NO;
}
return self;
}
- (void)start
{
if ([self isCancelled]) {
self.finished = YES;
return;
}
self.executing = YES;
[self main];
}
- (void)completeOperation
{
self.executing = NO;
self.finished = YES;
}
- (void)main
{
// start some asynchronous operation
// when it's done, call `completeOperation`
}
#pragma mark - Standard NSOperation methods
- (BOOL)isConcurrent
{
return YES;
}
- (void)setExecuting:(BOOL)executing
{
[self willChangeValueForKey:#"isExecuting"];
_executing = executing;
[self didChangeValueForKey:#"isExecuting"];
}
- (void)setFinished:(BOOL)finished
{
[self willChangeValueForKey:#"isFinished"];
_finished = finished;
[self didChangeValueForKey:#"isFinished"];
}
#end
Thus, with the following code, it won't start operation2 until the asynchronous task initiated in main in SomeOperation object, operation1, calls its completeOperation method.
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount=1;
NSOperation *operation1 = [[SomeOperation alloc] init];
NSOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
[someObject anotherSelector];
}];
[operation2 addDependency:operation1];
[queue addOperation:operation1];
[queue addOperation:operation2];

NSOperationQueue and passing data

In my app, I have my main file that creates a new instance of a class and then uses NSOperationQueue to run the class functions in the background, like so:
NSOperationQueue backgroundQueue = [NSOperationQueue new];
MyClass mc = [MyClass alloc];
NSInvocationOperation* operation = [[NSInvocationOperation alloc] initWithTarget:mc selector:#selector(runEvents) object:nil];
[backgroundQueue addOperation:operation];
MyClass then does stuff in RunEvents, but I'm having difficulty passing data to the UI. I'm just trying to update a label on my storyboard, which I can do in my main class that calls MyClass, but how do I update it from MyClass?
The typical answer is to create your class as a NSOperation subclass and give it a custom completion block. If your goal is update the UI or some model object in the completion block, make sure to dispatch that block back to the main queue:
// CustomOperation.h
#import <Foundation/Foundation.h>
typedef void(^CustomOperationCompletion)(NSString *string);
#interface CustomOperation : NSOperation
#property (nonatomic, copy) CustomOperationCompletion customOperationCompletion;
- (id)initWithCustomCompletion:(CustomOperationCompletion)completion;
#end
and
// CustomOperation.m
#import "CustomOperation.h"
#implementation CustomOperation
- (id)initWithCustomCompletion:(CustomOperationCompletion)completion {
self = [super init];
if (self) {
self.customOperationCompletion = completion;
}
return self;
}
- (void)main {
NSLog(#"%s starting", __FUNCTION__);
sleep(5);
NSString *string = [[NSDate date] description];
if (self.customOperationCompletion) {
[[NSOperationQueue mainQueue] addOperationWithBlock: ^{
self.customOperationCompletion(string);
}];
}
NSLog(#"%s ending", __FUNCTION__);
}
#end
Then you can invoke it with something like:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
CustomOperation *operation = [[CustomOperation alloc] initWithCustomCompletion:^(NSString *string) {
// update the UI with the results of the operation; here I'm just going to log it
NSLog(#"all done, string=%#", string);
}];
[queue addOperation:operation];
Clearly, change your CustomOperationCompletion parameters to include whatever you want to return (I'm just passing a string back).

How can I resolve issue with data not populating UITableView until I redeploy app after updating?

I've just setup a UITableview with Core Data and Grand Central Dispatch to update my app and display information through my fetchedResultsController. I have the application updating my database; however, the UITableView only gets populated once I redeploy the application to my phone through Xcode. For instance I run the update and everything works fine except I have an empty UITableView. Then I can close the app and click "Run" again through Xcode and when the app comes up the information is in the UITableView. I'm including the code below in hopes someone can help me discover why this is the case. If I need to include more code please just let me know. Thanks!
TeamTableViewController.m
- (NSFetchedResultsController \*)fetchedResultsController {
...
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchREquest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"stateCode" cacheName:nil];
self.fetchedResultsController = aFetchedResultsController;
...
}
-(IBAction) refreshList:(id)sender {
dispatch_queue_t queue = dispatch_queue_create("updateQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue,^ { [self updateFromXMLFile:#"https://path/to/file.xml"];});
dispatch_async(queue,^ { [self updateFromXMLFile:#"https://path/to/file2.xml"];});
dispatch_async(queue,^ { [self updateFromXMLFile:#"https://path/to/file3.xml"];});
}
- (BOOL)updateFromXMLFile:(NSString *)pathToFile {
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
XMLParser *parser1 = [[XMLParser alloc] initXMLParser];
XMLParser *parser2 = [[XMLParser alloc] initXMLParser];
XMLParser *parser3 = [[XMLParser alloc] initXMLParser];
BOOL success = FALSE;
NSURL *url = [[NSURL alloc] initWithString:pathToFile];
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
if ([pathToFile rangeOfString:#"people"].location != NSNotFound) {
NSManagedObjectContext * peopleMOC = [[NSManagedObjectContext alloc] init];
[peopleMOC setPersistentStoreCoordinator:[appDelegate persistentStoreCoordinator]];
NSNotificationCenter * notify = [NSNotificationCenter defaultCenter];
[notify addObserver:self selector:#selector(mergeChanges:) name:NSManagedObjectContextDidSaveNotification object: peopleMOC];
parser1.managedObjectContext = peopleMOC;
[xmlParser setDelegate: parser1];
success = [xmlParser parse];
if (success) {
NSError * error = nil;
#try {
[parser1.managedObjectContext save:&error];
} catch (NSException * exception) {
// NSLog logs the exception...
}
[NSNotificationCenter defaultCenter] removeObserver:self];
Return TRUE;
} else {
// NSLog logs errors
return FALSE;
}
} elseif ... { // other 3 use practically same code here }
[appDelegate saveContext];
}
-(void) mergeChanges:(NSNotification *) notification {
AppDelegate *theDelegate = [[UIApplication sharedApplication] delegate];
[[theDelegate managedObjectContext] performSelectorOnMainThread:#selector(mergeChangesFromContextDidSaveNotification:) withObject: notification waitUntilDone:YES];
}
UPDATE
I'm able to kind of get it to work by adding [theDelegate saveContext]; to the end of my -(void)mergeChanges method... This just doesn't seem like the proper way of doing it to me. Thoughts?
UPDATE 2
The above method worked one time but I've been unable to get it to replicate.
You should use the NSFetchedResultsControllerDelegate protocol to inform your view controller of any changes in the data.
Use controllerDidChangeContent: rather than the other methods. Like this, the results will be reflected once all the downloads have finished. Shorter incremental updates might get computationally expensive.
There is a very succinct "Typical Use" example in the delegate documentation.

Resources