Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 9 years ago.
Improve this question
As I get to some performance issues in my app and find that I use database access in a bad way.
So I decided to move to singleton pattern.
I need someone to review this code and confirm me that I made a good database access via singleton pattern or I am doing something wrong:
This is the class:
#interface DataAccessController : NSObject{
sqlite3 *databaseHandle;
}
+ (id)sharedManager;
-(void)initDatabase;
...
+ (id)sharedManager {
static DataAccessController *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (id)init {
if (self = [super init]) {
[self initDatabase]; // open database connection
}
return self;
}
...
AppDelegate.m
DataAccessController *d = [DataAccessController sharedManager];
Usage through the application every time I need data I use:
DataAccessController *d = [DataAccessController sharedManager];
NSMutableArray* data = [d getAllRecordedUnits];
The problem with database access is not whether you use a singleton pattern or not, but whether you access the database always from the main thread or also from other threads.
In fact, in most situations I use a singleton for database access (albeit by keeping the database connection in my app delegate). But you should make sure you always access the database on the main thread (which might be not performing well if you access it a lot and from different threads), or keep different contexts for different threads, synching them where needed.
Have a look here for an explanation:
Apple Core Data Documentation
Apple Core Data Performance Considerations
Apple Core Data Concurrency
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
for (ItemEntity *itemEntity in changedItemEntities) {
[[NetworkWrapper sharedInstance] uploadItemEntity:itemEntity userId:[itemEntity.userId unsignedIntegerValue] title:itemEntity.title completion:^(BOOL success, NSUInteger photoId, NSUInteger timeStamp) {
if (success) {
itemEntity.itemId = [NSNumber numberWithUnsignedInteger:photoId];
itemEntity.isUploaded = YES;
[self.context processPendingChanges];
NSError *error;
[self.context save:&error];
}
}];
}
What do these lines of code do in simple programming terms?
One of Objective-C's strengths is its verbose syntax, making the code very readable.
for (ItemEntity *itemEntity in changedItemEntities) {
This will perform the operation in braces ({}) for each ItemEntity object in changedItemEntities. Inside the braces, each object can be referred to as itemEntity.
[NetworkWrapper sharedInstance]
This gets a reference to the shared instance of a class called NetworkWrapper
uploadItemEntity:itemEntity userId:[itemEntity.userId unsignedIntegerValue] title:itemEntity.title completion:
This tells the shared instance to upload each entity, with a user ID and title derived from the entity object itself. There is a completion block, which is run once the operation is done, and looks like this:
^(BOOL success, NSUInteger photoId, NSUInteger timeStamp) {
if (success) {
itemEntity.itemId = [NSNumber numberWithUnsignedInteger:photoId];
itemEntity.isUploaded = YES;
[self.context processPendingChanges];
NSError *error;
[self.context save:&error];
}
This will run for each completed upload. That code will have a success flag (a boolean value), a photo ID (an integer) and a timestamp (also an integer). In your example the values are used to update the itemEntity object and then the context lines cause the Core Data store of the app to save its data to disk.
So basically you've got a bunch of things, you're performing an upload operation for each one, and when each upload is done, you're running another block of code.
It iterates through the collection named changedItemEntities, perhaps an NSArray—we can't know for sure as you don't show the definition, and for each ItemEntity in that collection, it sends the uploadItemEntity:userId:title:completion: selector to the NetworkWrapper singleton. The singleton is accessed via [NetworkWrapper sharedInstance], which is the standard Objective-C singleton pattern Apple uses. It will return the same pointer each time, so it could be moved out of the loop and stored in a local. The completion is a block (a lambda function) to be executed when the upload completes, presumably. Presumably the singleton passes the result of the upload operation—a boolean indicating success or failure, the photoId the server generated, and a timestamp, perhaps of the upload operation. That block would then do something with this information. In this case it seems to update the entity and commit some changes back via CoreData stuff.
You probably want to step through this code to understand it better. Set a break point on the first line and use the Step Over / Step Into buttons in Xcode to trace through it.
If you have specific questions, you should ask new specific questions.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I have a location app that already successfully asks the user to enable location services, and then can show them their coordinates on a button press as well. So I decided to play around with everything that is available in the CLLocationManager reference provided by xcode.
I decided to setup a bool method called "locationServicesEnabled". It returns a value of YES(1) or NO(0). I declared the method and then went to implement it. I am trying to have NSLog print the bool result out to the console when you open the app.
Here is how I declared the BOOL method in my ViewController.m file:
#interface ViewController () <CLLocationManagerDelegate>
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations;
+ (BOOL)locationServicesEnabled;
#end
And here is how I implemented the BOOL method in ViewController.m:
+ (BOOL)locationServicesEnabled{
[self locationServicesEnabled];
NSLog(#"%hhd", self.locationServicesEnabled);
return 0;
}
Why do you want to create an extra method? You could minimize the risk of errors by using already available ones: NSLog(#"Location services enabled: %d",[CLLocationManager locationServicesEnabled]);
I think your issue might be the fact that you are calling your function inside its self:
+ (BOOL)locationServicesEnabled
{
[self locationServicesEnabled]; <- should this be here
NSLog(#"%hhd", self.locationServicesEnabled);
return 0;
}
I'm using two different types of fmdb connections in my app:
FMDatabase for all READ queries and
FMDatabaseQueue for all UPDATE queries.
Both are handled by a singleton, which keeps both types open the whole time while the app is running.
Both, read and update queries, are used in different threads as some tasks in my app are progressed in background; like getting data from a server and inserting it in the db via FMDatabaseQueue in an own background thread - while reading some information from db via FMDatabase and updating an ViewController with it on the main thread.
My problem is that after inserting data into the db via FMDatabaseQueue the second connection (FMDatabase) does not return the updated information as it does not find them. But I know the data was inserted as I have checked the db with an db browser tool + no errors occur while inserting it. To avoid this, I have to close the FMDatabase db connection and reopen it to see the changes made by the other connection. Unfortunately when my app starts up there are a many inserts, updates + reads as a lot of new data is loaded from server which needs to be processed - so closing and opening the db every time an update was made occurs in many "database busy" messages.
I have used one single FMDatabaseQueue for all threads and executes (read, update) before but it was quite slow when using read queries with __block variables to get the resultset out of the callback while another thread does some inserts(between 50-100 in a single transaction).
On top of it the database is encrypted via sqlcipher - not sure if it's important but want to mentioned it. So every time i have to close and open the database I'm doing a setKey.
My question: Is it possible to use a setup with two different connection types on multiple threads and if yes, do I have to close and open the FMDatabase connection? Or is there a better solution for this usecase?
UPDATE
My code to perform an insert / update looks like
-(void) create:(NSArray *)transactions
{
NSMutableString *sqlQuery = [[NSMutableString alloc] initWithString:STANDARD_INSERT_QUERY];
[sqlQuery appendString:#"(transaction_id, name, date) VALUES (?,?,?)"];
FMDBDataSource *ds = [FMDBDataSource sharedManager];
FMDatabaseQueue *queue = [ds getFMDBQ];
[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
[db setKey:[ds getKey]]; // returns the key to decrypt the database
for (Transaction *transaction in transactions)
{
[db executeUpdate:sqlQuery, transaction.transactionId, transaction.name, transaction.date];
}
}];
}
and a read query
-(Transaction *)read:(NSString *)transactionId
{
NSString *sqlQuery = [[NSString alloc] initWithString:STANDARD_SELECT_QUERY];
Transaction *transaction = nil;
FMDBDataSource *ds = [FMDBDataSource sharedManager];
FMResultSet *rs = [[ds getFMDB] executeQuery:sqlQuery];
while ([rs next]) {
transaction = [[Transaction alloc] init];
[transaction setTransactionId:[rs stringForColumn:#"transaction_id"]];
[transaction setName:[rs stringForColumn:#"name"]];
}
[rs close];
return transaction;
}
The FMDBDataSource is a singleton holding both, FMDatabase and FMDatabaseQueue, connections
- (FMDatabaseQueue *)getFMDBQ
{
if (self.fmdbq == nil)
{
self.fmdbq = [FMDatabaseQueue databaseQueueWithPath:[self getDBPath]];
}
return self.fmdbq;
}
- (FMDatabase *) getFMDB
{
if(self.fmdb == nil)
{
self.fmdb = [FMDatabase databaseWithPath:[self getDBPath]];
[self openAndKeyDatabase]; // opens the db and sets the key as the db is encrypted
}
return self.fmdb;
}
As I said, when using this code the FMDatabase connection does not get the information which was inserted via FMDatabaseQueue.
Personally, I would suggest using the single FMDatabaseQueue for both threads and let the queue coordinate the actions on the two threads. That's what it was created for. It completely eliminates those "database busy" problems.
On your performance update, if doing a bulk update, are you using the FMDatabase method beginTransaction before the update and commit at the end? Or use the inTransaction method. Inserting 10,000 records without transactions in my test takes 36.8 seconds, but with transactions it takes 0.25 seconds.
Or, if your bulk update is slow by necessity (e.g. you're downloading some big data source from a web service using some streaming protocol), you can either:
Load all the results into memory first, with no database interaction, and then use the bulk update with transactions as described in the previous paragraph; or
If your database updates are necessarily constrained by a slow network connection, then use separate inDatabase calls so that it doesn't tie up the FMDatabaseQueue while downloading data from your web service.
Bottom line, through the use of transactions or the judicious use of separate inDatabase calls, you can minimize how long your background operation is ties up the FMDatabaseQueue and you can achieve synchronized multi-threaded interaction with your database without blocking your UI too significantly.
I have spent lots of hours trying to making the same, when I found your post.
I do not know if you have already find a solution.
After a lot of tries and searches, I had to gave up and I'am looking for another solution now.
But, I would like to share my conclusions.
SQLite has a function to define READONLY or READWRITE mode. FMDB implements like openWithFlags.
[db openWithFlags:SQLITE_OPEN_READONLY|SQLITE_OPEN_NOMUTEX];
Setting these flags do not permits reading while wrinting even if we set these flags.
I could accomplish reading+writing (different connections) setting my database to use WAL journal_mode (http://www.sqlite.org/wal.html).
BUT, SQLCipher screw everthing up.
Conclusion READING + WRITING with 2 connections:
FMDB + openWithFlags = BE SAD AND ANGRY
FMDB + openWithFlags + WAL jornal_mode = BE HAPPY
FMDB + SQLCipher + openWithFlags = BE SAD AND ANGRY
FMDB + SQLCipher + openWithFlags + WAL jornal_mode = BE SAD AND ANGRY
As my application needs security, I do not know what to do yet.
Well, I hope it helps.
Best
Hami.
Trying to implement an app which sends offline data stored on local db to web server when connected to internet. I use the code shown below. As far I have tested it works fine, not sure it will work fine for huge number of records. I would like to know whether any tweaking on this code may increase the performance???
NOTE
I know this would be a worst code for offline sync purpose, so trying
to tweak it better.
Its a single way synchronization, from app to server.
-(void)FormatAnswersInJSON {
DMInternetReachability *checkInternet = [[DMInternetReachability alloc] init];
if ([checkInternet isInternetReachable]) {
if ([checkInternet isHostReachable:#"www.apple.com"]) {//Change to domain
responseArray = [[NSMutableArray alloc] init];
dispatch_async(backgroundQueue, ^(void) {
NSArray *auditIDArray = [[NSArray alloc] initWithArray: [self getUnuploadedIDs]];
for (int temp = 0; temp < [auditIDArray count]; temp ++) {
// Code to post JSON to server
NSURLResponse *response;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if (!error) {
NSString *responseID = [[NSString alloc]initWithData:urlData encoding:NSUTF8StringEncoding];
if ([responseID isEqualToString:#"ERROR"]) {
//Error uploading records
} else {
[responseArray addObject:responseID];
}
} else {
//Error
return;
}
}
dispatch_async( backgroundQueue, ^{
/* Based on return code update local DB */
for (int temp = 0; temp < [responseArray count]; temp ++) {
[self updateRecordsForID:[auditIDArray objectAtIndex:temp] withID:[responseArray objectAtIndex:temp]];
}
});
});
}
}
}
- (void)upload { //Called when internet connection available
if(backgroundQueue){
dispatch_suspend(backgroundQueue);
dispatch_release(backgroundQueue);
backgroundQueue = nil;
}
backgroundQueue = dispatch_queue_create("com.XXXX.TestApp.bgqueue", NULL);
dispatch_async(backgroundQueue, ^(void) {
[self FormatAnswersInJSON];
});
}
If this code were sitting in front of me, my approach would be:
Look at the use cases and define 'huge number of records': Will 50 record updates at a time occur regularly? Or will it be in 1s and 2s? Do my users have wifi connections or is it over the paid network?, etc.
If possible, test in the wild. If my user base was small enough, gather real data and let that guide my decisions, or only release the feature to a subset of users/beta tests and measure.
If the data tells you to, then optimize this code to be more efficient.
My avenue of optimization would be doing group processing. The rough algorithm would be something like:
for records in groups of X
collect
post to server {
on return:
gather records that updated successfully
update locally
}
This assumes you can modify the server code. You could do groups of 10, 20, 50, etc. all depends on the type of data being sent, and the size.
A group algorithm means a bit more pre-processing client side, but has the pro of reducing HTTP requests. If you're only ever going to get a small number of updates, this is YAGNI and pre-mature optimization.
Don't let this decision keep you from shipping!
Your code has a couple of issues. One convention is to always check the return value before you test the error parameter. The error parameter might be set - even though the method succeeded.
When using NSURLConnection for anything else than a quick sample or test, you should also always use the asynchronous style with handling the delegate methods. Since using NSURLConnection properly may become quickly cumbersome and error prone, I would suggest to utilize a third party framework which encapsulates a NSURLConnection object and all connection related state info as a subclass of NSOperation. You can find one example implementation in the Apple samples: QHTTPOperation. Another appropriate third party framework would be AFNetworking (on GitHub).
When you use either the async style with delegates or a third party subclass, you can cancel the connection, retrieve detailed error or progress information, perform authentication and much more - which you can't with the synchronous API.
I think, once you have accomplished this and your approach works correctly, you may test whether the performance is acceptable. But unless you have large data - say >2 MByte - I wouldn't worry too much.
If your data becomes really large, say >10 MByte you need to consider to improve your approach. For example, you could provide the POST data as file stream instead a NSData object (see NSURLRequest's property HTTPBodyStream). Using a stream avoids to load all the POST data into RAM which helps alleviate the limited RAM problem.
If you have instead smaller POST data, but possibly many of them, you might consider to use a NSOperationQueue where you put your NSOperation connection subclass. Set the maximum number of concurrent operations to 2. This then may leverage HTTP pipelining - if the server supports this, which in effect reduces latency.
Of course, there might be other parts in your app, for example you create or retrieve the data which you have to send, which may affect the overall performance. However, if your code is sound and utilizes dispatch queues or NSOperations which let things perform in paralel there aren't many more options to improve the performance of the connection.
I have an application that downloads information from a web service and caches it in memory. Specifically, my singleton cache class contains an instance variable NSMutableDictionary *memoryDirectory which contains all of the cached data. The data in this cache can be redownloaded easily, so when I receive a UIApplicationDidReceiveMemoryWarningNotification I call a method to simply invoke
- (void) dumpCache:(NSNotification *)notification
{
memoryDirectory = nil;
}
I’m a little worried about the thread safety here. (I’ll admit I don’t know much about threads in general, much less in Cocoa’s implementation.) The cache is a mutable dictionary whose values are mutable dictionaries, so there are two levels of keys to access data. When I write to the cache I do something like this:
- (void) addDataToCache:(NSData *)data
forKey:(NSString *)
subkey:(NSString *)subkey
{
if (!memoryDirectory)
memoryDirectory = [[NSMutableDictionary alloc] init];
NSMutableDictionary *methodDictionary = [memoryDirectory objectForKey:key];
if (!methodDictionary) {
[memoryDirectory setObject:[NSMutableDictionary dictionary] forKey:key];
methodDictionary = [memoryDirectory objectForKey:key];
}
[methodDictionary setObject:data forKey:subkey];
}
I’m worried that sometime in the middle of the process, dumpCache: is going to nil out the dictionary and I’m going to be left doing a bunch of setObject:forKey:s that don’t do anything. This isn’t fatal but you can imagine the problems that might come up if this happens while I’m reading the cache.
Is it sufficient to wrap all of my cache reads and writes in some kind of #synchronized block? If so, what should it look like? (And should my dumpCache: be similarly wrapped?) If not, how should I ensure that what I’m doing is safe?
Instead of using an NSMutableDictionary, consider using NSCache, which is thread safe. See this answer for example usage. Good luck!