I'm trying to update PFObjects with server information, basically to discard local changes when user cancels actions, but I''m having a hard time. I'm not sure if I'm missing something or it's a bug, it has been a long programming session.
Here is a test code:
PFObject *object = [PFObject objectWithoutDataWithClassName:#"UserRecipes" objectId:#"1NOxhVZVXJ"];
object[#"instructions"] = #"TEST INSTRUCTIONS";
NSLog(#"%#", object[#"instructions"]);
[object fetch];
NSLog(#"%#", object[#"instructions"]);
I've tried with different objects and classes to make sure it isn't something related to acl. By the way, in one particular PFQueryTableViewController, when I change the object with the method above, no success, but when I leave the controller and enter again, forcing everything to be reloaded, the object is fetched with data from the server and local changes are gone.
UPDATE:
As I temporary solution, I'm using [PFObject revert] to discard changes and it works. But I would really prefer to perform tasks in background using fetchInBackground.
Related
First of all I'm using MagicalRecord to manage my Core Data. So right now my database layer works just great and it saves changes every time the change occurs. For example: I'm adding a new entry to a table and it gets written to db file stored on my hard disc right away. What I want to achieve is to keep all the changes in-memory, and write them to db file only on "save" command click.
I figured that the call that does the db file writing is:
[managedObjectContext MR_saveToPersistentStoreAndWait];
So as I figured, I can do all my modifications without calling that method, and then on "save" click, call that method. However, it works only if the thread wasn't changed. Every time, the thread changes the ManagedObjectContext gets reset or recreated, and I lose all my data.
EDIT: just found method in Magical Record:
[NSPersistentStoreCoordinator MR_coordinatorWithInMemoryStore];
which is what I need for the first part of my problem. Although, I don't know how to change from that type of the coordinator to
[NSPersistentStoreCoordinator MR_coordinatorWithSqliteStoreNamed:objectModelName];
without losing any data.
Does anyone know how to do this right?! Any kind of help is really appreciated!
Check these options:
1- There is setupCoreDataStackWithInMemoryStore which might be helpful if you want to run everything in memory.
2-You can save in the background:
[MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *localContext){
}];
3-To get the default context or the one for the thread
[NSManagedObjectContext MR_defaultContext];
[NSManagedObjectContext MR_contextForCurrentThread];
I figured it out by doing serious research. I need to use
NSPersistentStoreCoordinator *coordinator = [NSPersistentStoreCoordinator MR_coordinatorWithInMemoryStore];
to have my data "hang" before user clicks on save, where I do the following:
// psc - my current persistentStoreCoordinator; urlForStore - place where I'm gonna store SQLite
[psc migratePersistentStore:[psc persistentStores][0] toURL:urlForStore options:nil withType:NSSQLiteStoreType error:&error];
I have a variable that gets changed a lot that I have therefore pulled out of my core data database (it's just an int but it gets updated very regularly).
My app is NSPersistentDocument based and when the user closes a document I would like to save this variable into the database so it persists.
I did the following:
- (void) windowWillClose:(NSNotification *)notification
{
[self.managedObjectContext performBlockAndWait:^(void) {
self.myDatabase.myNumber = [NSNumber numberWithInt:self.myTempInt];
[self.managedObjectContext save:nil];
}];
}
This works in terms of saving the data, and when a new document opens I can set myTempInt from the database value.
However, it has introduced a problem in that the document no longer deallocates – it seems that referencing self.myDatabase at the moment the document is closing creates a retain that means ARC never kicks in.
Is there a better place to do this kind of thing that won't be a problem for reference counting or am I approaching it all wrong?
Update:
I've been thinking this over and realised that it would be far better to do this operation whenever the document autosaves, rather than when it closes. Is there something like 'documentWillAutosave' I could use?
I have some NSManagedObject subclass in my app, and I'm trying to understand when and how to save changes.
I will try to explain myself, for example class A is NSManagedObject subclass.
During app life cycle I do:
App launched
...
Create an instance of class A
...
Change some properties of A instance
...
App go to background
...
App becomes active again
...
Change some more properties of A instance
...
App terminates
When do I need to call [context save:] ??
Do I call it after every change in A instance? Or maybe I call it only when app go to background?
Should I call it after creation or deletion of any A instance?
A nice approach is place UIManagedDocument in your AppDelegate. Then you can call [context save] whenever some change occurs in the app (like a crash). The order I like to follow is something like:
Create UIManagedDocument object (in application did load or wherever)
and assign it to a property
Setup the document (check whether it exist on disk or is already open, etc.. and respond accordingly)
Pass the UIManagedObjectContext to the initial UIViewController in your app (from there you can pass the context to other view controllers)
UIManaged document will save the context for you.
Take a look at the UIManagedDocument documentation to configure persistent store options (you send an NSDictionary of options to your UIManagedDocument instance, see the first example through the link below).
UIManagedDocument documentation:
http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIManagedDocument_Class/Reference/Reference.html
Also see the CoreData lecture and demo (lectures 13 and 14) of the iPhone and iPad application development course with Paul Hegarty available free on iTunesU (Fall 2011).
The data will not be saved to the persistent store until you call save. So, it depends on what you want in your app. If you want it to be able to recover the last value it ever had, then you should save after each modification.
Easy change is to just save after making modifications.
You could do something a bit more fancy, like only save after some set amount of time, so many changes are grouped together... and catch any event that will put your app in the background and then save...
But, that's what UIManagedDocument does automatically for you, so you could just use that instead.
Depending on the amount of changes that you make and the volume of data that needs to be saved with each change, yo can choose to save a little or a lot. If you are just saving a string or a number or a bool, then go ahead and call save: on your context as soon as the changes were made.
If it is a lot of data, you may want to coalasce your changes and then save it on a background queue so that you are not blocking the main queue. This way you are not waiting to go to the background to perform your saves.
Tim
In my In App purchase the case SKPaymentTransactionStateRestored: method does not get called when the purchase is restored, instead, case SKPaymentTransactionStatePurchased: is being called, but would like to show the user different information, when the purchase is restored.
[queue restoreCompletedTransactions] will trigger SKPaymentTransactionStateRestored state, where queue is SKPaymentQueue. You should not expect this to be called after your user confirms transactions. Instead user would be warned by the device if he had already purchased the product.
I've a clue. The method (paymentqueue:updatedTransactions:) and especcially the SKPaymentTransactionStateRestored: state is getting called, when i create a SKProductRequest and start loading it.
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers: [NSSet setWithArray: productIdentifiers]];
request.delegate = self;
[request start];
I'll have a look on Apples solution and see, if i can find any differences.
You can implement paymentQueueRestoreCompletedTransactionsFinished: to determine if a restore was finished:
http://developer.apple.com/library/ios/#documentation/StoreKit/Reference/SKPaymentTransactionObserver_Protocol/Reference/Reference.html
In case, it's not solved yet, you might do it with a logical trick. When the restore is beeing triggered, remember it with a boolean flag (or a enum state, if you prefer) and show a different message when the SKPaymentTransactionStatePurchased is beeing triggered. You might implement the two delegate methods
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
and
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
to find out, when the restore has been finished. And to be save in this case, you might not let the user make any purchases, while the restore is in progress. You could show the user something like this, so the user sees, that something is currently happening.
This is what i did and it works for me.
I have written this trivial action method associated to a textfield.
Every time I enter text into a textfield a search in a PDF is performed and PDFView automatically scroll to selection:
- (IBAction) search:(id)id
{
NSString *query = [self.searchView stringValue]; // get from textfield
selection = [document findString: query fromSelection:NULL withOptions:NSCaseInsensitiveSearch];
if (selection != nil)
{
[self.pdfView setCurrentSelection:selection];
[self.pdfView scrollSelectionToVisible:self.searchView];
}
}
Problem is that after 3 or 4 searches I get EXC_BAD_ACCESS on row (i).
If I debug I see that query contains an NSCFString and not an NSString.
I think it is a memory management problem..but where?
I replicated the same issue inside a trivial testcase:
#interface PDFRef_protoTests : SenTestCase {
#private
PDFDocument *document;
}
........
- (void)setUp
{
[super setUp];
document = [[PDFDocument alloc] initWithURL: #"a local url ..."];
}
- (void)test_exc_bad_access_in_pdfdocument
{
for (int i =0 ;i<100; i++)
{
NSString *temp;
if (i % 2 == 0) temp = #"home";
else if (i % 3 ==0) temp = #"cocoa";
else temp=#"apple";
PDFSelection *selection = [document findString: temp
fromSelection:nil
withOptions:NSCaseInsensitiveSearch];
NSLog(#"Find=%#, iteration=%d", selection, i);
}
}
Update:
1) It seems that it happens also if I use asynchronous search (method beginFindString: withOptions) every time I perform second search.
2) I found a similar problem to mine in MacRuby Issue Tracking: http://www.macruby.org/trac/ticket/1029
3) It seems that if I disable temporarily garbage collection it works but memory goes up.
I wrote something like:
[[NSGarbageCollector defaultCollector] disable];
[[NSGarbageCollector defaultCollector] enable];
surrounding search code
Another Update
Very weird thing is that sometimes all works. Than I clean and Rebuild and problem arises again. From a certain point of view is is not 100% reproducible. I suspect a bug in PDFKit or some compiler setting I have to do
Update Again
Dears it seems very crazy. I'd concentrate on testcase which is very trivial and which replicates easily the problem. What's wrong with it? This testcase works only if I disable (by code or by project setting) GC
Another Update
Boys it seems a bug but I downloaded an example called PDFLinker from Apple website (http://developer.apple.com/library/mac/#samplecode/PDFKitLinker2/Introduction/Intro.html#//apple_ref/doc/uid/DTS10003594). This example implements a PDFViewer. Code of my app and this example are quite similars. For the same search action on same PDF, my memory rises at 300/400 MB while PDFLinker rises at 190MB. Clearly there is something wrong in my code. But I am comparing it bit by bit and I don't think I am inserting memory leaks (and Instrument doesn't give me any evidence). Maybe is there some project-wide setting ?
Update Yet
Changing from 64 bit to 32 bit memory consumption lowered. Surely there is a problem with 64bit and PDFKit. BTW still EXC_BAD_ACCESS on second search
SOLUTION
Crucial point is that PDFKit with Garbage collection is bugged.
If I disable GC all works correctly.
I had another issue that had complicated my analysis: I disabled GC on Project Setting but GC remained enabled on Target Settings. So Apple's example PDFLinked2 worked while mine not.
I agree you have found a bug in PDFKit.
I got various forms of errors (segmentation fault, selector not understood, etc) running your test case. Wrapping the code in #try/#catch doesn't prevent all errors associated with this method.
I also got errors printing the log message.
To work around the bug(s), I suggest you disable GC during your invocation of -findString:fromSelection:, as you've already discovered.
Also, be sure to make copies of the values of interest from selection before re-enabling GC. Don't just copy selection either.
If you conduct searches from multiple places in your code I also suggest you extract a separate method to perform the search. Then you can invoke that one to conduct the searches for you without duplicating the GC disable/enable nesting.
This sort of thing is usually evidence that you're hanging onto a pointer to an object that has been destroyed. Turn on zombie objects (with NSZombieEnabled) to see exactly where and when you're accessing a bad object.
Judging from your screen shot it doesn't seem like you have NSZombie turned on. Probably the reason why it doesn't help you. Here's how you turn it on:
How to enable NSZombie in Xcode?
The screenshot you provided was otherwise very useful, but you really need NSZombie to figure out this kind of errors. Well, unless it's obvious, which it isn't from the code you posted.
EDIT: I read the comment that you're using garbage collection. I'm an iOS developer, so I have very limited experience with garbage collection in Objective-C, but as far as I understand NSZombie doesn't work in a garbage collected environment.
I'm not sure it should be possible to get EXC_BAD_ACCESS in a garbage collected environment, unless you create your own pointer and try to call methods on it without having created an object and I don't see why you would do that.
I've heard that some frameworks doesn't work well with garbage collection, but I wouldn't think PDFKit was among them. Anyway, the solution might be to not use garbage collection. Perhaps you should file a bug report with Apple.
keep PDFSelection *selection as a member variable and pass it in fromSelection: instead of nil.
It is possible that PDFDocument keeps the returned PDFSelection instance to improve the performance.
Did you try retaining the searchview stringvalue object before using it?
As you say it happens when you type fast and it happens for async calls, it is possible that the object stringValue is pointing to is being released between the time your query object is pointing to it, and the time you use it int the search.
You could try something like this to see if the problem persists:
- (IBAction) search:(id)id
{
NSString *query = [[self.searchView stringValue] retain]; // get from textfield
selection = [document findString: query fromSelection:NULL withOptions:NSCaseInsensitiveSearch];
if (selection != nil)
{
[self.pdfView setCurrentSelection:selection];
[self.pdfView scrollSelectionToVisible:self.searchView];
}
[query release];
}
Of course there is also the possibility that document is relased. How do you declare it? is it a property with retain? Can it be released by the time you are searching?
EDIT:
I see that you posted the code with the second parameter as NULL, but in your screenshot, this value is nil.
The documentation says you should use NULL when you want to start the search from the beginning.
http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Reference/QuartzFramework/Classes/PDFDocument_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40003873-RH2-DontLinkElementID_1
And as the compiler interprets nil and NULL differently, this could be leading to some weird behavior internally.