Re: Core Data mergeChangesFromContextDidSaveNotification: does not *completely* update the context ?
On Dec 19, 2010, at 9:49 PM, Aurélien Hugelé wrote: Hi! I think mergeChangesFromContextDidSaveNotification: does not work as most people expect: I have a mainthread and a subthread. My subthread updates a managed object (change one of the property value) and save. In the mainthread, I use [mainThreadContext mergeChangesFromContextDidSaveNotification:subThreadNotification]; and it merges the main thread context as expected (binded UI is updated) What is not expected is : 1/ asking the main thread for its updatedObjects (just after the mergeChangesFromContext... but before the save:) does not show the changes made in the subthread! So updated objects in subthread are not seen as updated in the main thread after the mergeChangesFromContext call! 2/ Saving the main thread generates a did save notification, that does not contain changes made in the subthread (and merged) ! The subthread is temporary (does it job in an NSOperation that terminates quickly), its context is also temporary. In the mainthread, *many* controllers are observers of the did save notification of the main thread context. How am I supposed to make them listen to changes made in a temporary, dumb, subthreaded managed context without using mergeChangesFromContext... call ??? I'm pertty sure, most developers expect the mergeChangesFromContextDidSaveNotification: API to really merge the data in the main thread context and set the merged updated objects as *really* updated, as if the change really occured in the main thread! The method is merging the state into the receiving context purely from the perspective of results. Deleted objects are deleted, updated objects have new values, inserted objects exist. It does not replay the individual changes, nor does it guarantee any particular path or implementation for getting the state of MOC B to look like the state of MOC A. For efficiency, it prefers refreshing existing objects from the PSC's cache over replaying individual changes whenever possible. Refreshing is observable by KVO. These objects are also noted in the NSManagedObjectContextObjectsDidChangeNotification with the NSRefreshedObjectsKey. This is how NSArrayController observes these kinds of events. The objects aren't updated in the sense that they'll be saved again. Because they won't. They've already been saved a first time. They *were* updated, and NSManagedObjectContext's -updatedObjects reports the state about next upcoming save, not the last save. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: KVO and Core data
Hi all, I have a KVO registration like this: [self addObserver:self forKeyPath:@toOne.attribute options:0 context:NULL]; I establish this on -awakeFromInsert or -awakeFromFetch, and have the corresponding removeObserver called on -willTurnIntoFault. The problem is that when I do a Save As on my document, it migrates the persistent store, which appears to cause the object at the destination of the toOne relationship to be turned into a fault before the object that registered as an observer is. Now when I check the observationInfo for the object that is being faulted (at the end of the toOne relationship), it has itself registered as an observer for attribute. I guess this means that behind the scenes, the KVO establishes observation information automatically at every level of a key path (something I hadn't realized before, but which appears to make sense). Then something behind the scenes tries to access the attribute on an object that has been turned into a fault, and invalidated, and everything comes crashing down. So I have two questions: 1. Is it the right way for me to be adding observation on the awake methods and removing it on willTurnToFault? 2. If this is OK, then what could be affecting the system such that it doesn't remove the observation information in the destination of the to-one relationship? I'm wondering if there could be something in my data model that could be affecting it. This relationship is modeled as a one to one, with a delete rule of cascade from the parent to the child, and the inverse relationship is also modeled, with a deny rule. There's also another optional relationship from the child to the parent with a deny rule, modeled in one direction only (and not used in the data I am testing with anyway). Your problem here is this MOC is being torn down, and the toOne managed object is already toast. So it's not possible to get its attribute. KVO isn't really in a position to deal with this. Probably the best solution is for the toOne object to traverse the inverse and tell it's parent when to add or remove the KVO observation. So this way when attribute is nuked, parent stops observing it. Another possibility is to just have the accessor methods on toOne for the setter for attribute traverse the inverse manually and inform the parent outside of KVO. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Core Data : Multiuser ?
Can more than one process be accessing the same Core Data sqlite file? This post from author Marcus Zarra says no∑ http://forums.pragprog.com/forums/90/topics/1476 But this post from Ben Trumbull seems to say yes, as long as the two processes are accessing it via the same filesystem: http://www.cocoabuilder.com/archive/cocoa/184606-core-data-file-sharing-via-an-afp-alias.html Which one am I misunderstanding? There is a big difference between multiple processes accessing the same Core Data file and multiple machines. Multiple processes on the same local machine work fine. Several Apple products do this. Multiple processes across multiple physical machines will not work well (AFP) or at all (NFS). So we don't recommend targeting a multi-user configuration. As I mentioned in that earlier post, your best bet is probably write the proper service that uses Core Data, and vends to your client processes via IPC (like a web service). The client processes might independently use Core Data for local caching (replication). It possible, but inefficient, for a very limited number of clients to share over AFP. NFS doesn't work correctly at all. This is restricted by file caching issues underneath us. There are a lot of limitations and sharp edges here, so we actively recommend against multiple computers simultaneously accessing the same db. Support for it is disabled by default for projects built with a deployment target = 10.6 - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Core Data : Multiuser ?
p.s. http://www.cocoabuilder.com/archive/cocoa/283632-coredata-database-sharing-and-migration.html#283632 - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Core Data : Multiuser ?
When you add the persistent store to the coordinator, in the store options dictionary, use the option NSSQLitePragmasOption (which is a sub dictionary of pragma keys values) to pass the @lock_proxy_file key with a value of NSNull. - Ben On Aug 7, 2010, at 7:17 AM, Hunter Hillegas wrote: Given those limitations, how does one enable support for a 10.6+ target? On Aug 6, 2010, at 11:38 PM, Ben Trumbull wrote: It possible, but inefficient, for a very limited number of clients to share over AFP. NFS doesn't work correctly at all. This is restricted by file caching issues underneath us. There are a lot of limitations and sharp edges here, so we actively recommend against multiple computers simultaneously accessing the same db. Support for it is disabled by default for projects built with a deployment target = 10.6 ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Core Data : Multiprocess (was Multiuser) ?
Sorry. I see I've created confusion by putting Multiuser in the subject. I should have said Multiprocess yeah. Yes, several Apple frameworks use Core Data databases from multiple processes simultaneously with a single user account and single physical machine. Later, a reference is made to the iCal, Mail and Address Book apps. That's what I'm interested in. Specifically, an app + a background process, same Mac, same user, sharing a non-document sqlite file. So it appears that all should work, presumably as described in the Change Management section of Core Data Programming Guide. yes, that works out of the box. One thing still bothers me. Twice in that document, it mentions managed object contexts in the *same application*. What if they are in *different processes*? Possibly the author just wasn't thinking of that? On the same physical machine and local file system, the behavior between MOCs in the same process but with different PSCs and MOCs in different processes is effectively the same. I've done some more testing since my original post and it appear does appear to work ˆ the errors I was seeing yesterday were Could not merge changes errors (133020 in NSCocoaErrorDomain), and I presume these were due to the fact that I had not set a merge policy on these managed object contexts. yes. That's correct. But I have yet to reproduce and prove this. Any additional assurance that what I'm doing should work would be appreciated. There are a few things to be mindful of: (1) creating the db originally requires additional coordination (like your own NSDistributedLock). Basically, there can be a race condition between the 2 processes creating the file. After creating the db, you'll want to save immediately to force the schema and UUID to get persisted so other processes see the same one at that path (2) store metadata does not have optimistic concurrency control like the managed objects do (e.g. no version tracking). you'll want to minimize any custom metadata, and perhaps store it in an entity with a single row. (3) you may want to refresh data more aggressively. If so, you should NEVER use -refreshObject:mergeChanges:NO on objects that are -isInserted, -isUpdated, or -isDeleted. You should use -refreshObject:mergeChanges:YES on any dirty managed objects. Depending on how much data you want merged into the other process, you may wish to send a little IPC or NSDistributedNotification with bread crumbs to only refresh the changed objects of importance. It's a common mistake to post a generic notification to the other process that says world changed; have fun. (4) As with multi-threading, deleting data out from underneath the other MOCs can create challenges (faults that throw because the requested data has been nuked into oblivion). You'll probably want to use prefetching and setReturnsObjectsAsFaults:NO more than usual as well as this method added in 10.6 instead of -objectWithID: /* returns the object for the specified ID if it is already registered in the context, or faults the object into the context. It might perform I/O if the data is uncached. If the object cannot be fetched, or does not exist, or cannot be faulted, it returns nil. Unlike -objectWithID: it never returns a fault. */ - (NSManagedObject*)existingObjectWithID:(NSManagedObjectID*)objectID error:(NSError**)error __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0); - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Cant read second table from same sqlite database iphone
I'm afraid I can't be much more help if you don't have the return codes from each of the SQLite statements. I do note in the second method, you're asking for column values out of order, which is pretty unusual. - Ben On Apr 10, 2010, at 6:42 PM, charisse napeÿf1as wrote: I was able to successfully retrieve data on the first table. But I can't seem to retrieve from the second table. I know it has data inside it because I used an SQLite Browser to view the data. I also tried performing queries there and it worked fine. Only when I used the queries in the code that it doesn't work. Below is the code writing the database in the Users/Documents folder for Iphone - (void) createDatabaseIfNeeded { NSError * error; //gets access to the file system NSFileManager * fileManager = [NSFileManager defaultManager]; // gets the complete users document directory path NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); // gets the first path in the array NSString * documentsDirectory = [paths objectAtIndex:0]; // create the complete path for the database file NSString * databasePath = [documentsDirectory stringByAppendingString:@/Database.sql]; DebugLog(databasePath = %s\n,[databasePath UTF8String]); // check if file exists or not BOOL success = [fileManager fileExistsAtPath:databasePath]; if (success) return; // the database does not exist so copy it in the users documents directory NSString * dbPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@/Database.sql]; DebugLog(dbpath is %s\n,[dbPath UTF8String]); //copy the database file to ther users docuement directory success = [fileManager copyItemAtPath:dbPath toPath:databasePath error:error]; if (!success) NSAssert(0,@Failed to copy Database!\n); } From: Ben Trumbull trumb...@apple.com To: cnape...@yahoo.com Cc: Cocoa dev cocoa-dev@lists.apple.com Sent: Sunday, April 11, 2010 9:27:46 Subject: re: Cant read second table from same sqlite database iphone Hello All, I am having trouble reading the second table from my database because it returns nothing even if there is data inside it. How do you know it has data inside it ? A common mistake is to try to write to databases in the read only part of an application's sandbox on the iphone. Where in the sandbox is the database ? - (NSMutableArray*) getProvinces { NSMutableArray * data = [[NSMutableArray alloc] init]; const char * sql = SELECT * FROM Provinces; sqlite3_stmt * statement; //prepare the select statement int returnValue = sqlite3_prepare_v2(mDatabase, sql, -1, statement, NULL); DebugLog(return value = %d\n,returnValue); if (returnValue == SQLITE_OK) { //sqlite3_bind_int(statement, 1, regionID); //loop all the rows returned by the query while (sqlite3_step(statement) == SQLITE_ROW) { You don't check the return codes here properly. sqlite3_prepare_v2() can return SQLITE_BUSY, or SQLITE_SCHEMA, or SQLITE_MISUSE. More importantly, you don't check if sqlite3_step() returns SQLITE_DONE which would indicate that you have 0 rows in this table. - Ben New Email names for you! Get the Email name you've always wanted on the new @ymail and @rocketmail. Hurry before someone else does! - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Cant read second table from same sqlite database iphone
Hello All, I am having trouble reading the second table from my database because it returns nothing even if there is data inside it. How do you know it has data inside it ? A common mistake is to try to write to databases in the read only part of an application's sandbox on the iphone. Where in the sandbox is the database ? - (NSMutableArray*) getProvinces { NSMutableArray * data = [[NSMutableArray alloc] init]; const char * sql = SELECT * FROM Provinces; sqlite3_stmt * statement; //prepare the select statement int returnValue = sqlite3_prepare_v2(mDatabase, sql, -1, statement, NULL); DebugLog(return value = %d\n,returnValue); if (returnValue == SQLITE_OK) { //sqlite3_bind_int(statement, 1, regionID); //loop all the rows returned by the query while (sqlite3_step(statement) == SQLITE_ROW) { You don't check the return codes here properly. sqlite3_prepare_v2() can return SQLITE_BUSY, or SQLITE_SCHEMA, or SQLITE_MISUSE. More importantly, you don't check if sqlite3_step() returns SQLITE_DONE which would indicate that you have 0 rows in this table. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Problem with vCard and AddressBook ABPerson UID, HELP!!
've been fighting now with the AdressBook API for a while and found a disturbing problem. Maybe someone can help. I'm trying to use the AddressBook as my main person database in my application. I've create a small function that accepts drag - drops from the Address book to add a new person in my app. So far so good. I'm trying after to show in my application details about that linked person and that's where everything falls down. On drop, I read the vCard created by the AddressBook using this line: ABPerson* aPerson = [[ABPerson alloc] initWithVCardRepresentation:filedata]; That always creates a brand new contact with a brand new uniqueID. As you observe. I'm pretty sure it's not an appropriate way to pass around a *reference* to an existing contact. That's what -[ABRecord uniqueId] is for. You should get the uniqueId, pass that around, and then use -[ABAddressBook recordForUniqueId:] to look up the contact. - Ben When doing this, the UID of my person gets re-generated. I wanted to use the UID as my reference to my original address book record and dig the information using it. But since its re-generated upon read of the vCard, no luck. So the call: ABAddressBook* addressBook = [ABAddressBook addressBook]; ABRecord* abRecord = [addressBook recordForUniqueId:personId]; always return null. But, if I use the sharedAddressBook instead, my UIDs still get re-generated but this guy (the sharedAddressBook) can find the records but only in the same execution of the application. Upon restart, same old no-match problem. There must be something i REALLY don't understand about the AB but can't find anything more, out of ideas. Anybody knows what's going on? Any way we can prevent my vCard initialization from re-genrating the UID? If not, then what would be the correct way to refer to AB records from another application? Running a search based on names sounds bad compared to using direct pointers to records, the UID. ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Finding managed objects by URI representation
On Apr 5, 2010, at 8:18 AM, Gideon King wrote: On 05/04/2010, at 6:51 AM, Ben Trumbull wrote: No, this is going the wrong way. The objectID is the object's identity in the persistent store (e.g. primary key). You don't need to store pieces of it somewhere else. NSPredicate *predicate = [NSPredicate predicateWithFormat:@self == %@, myobjectid]; or NSPredicate *predicate = [NSPredicate predicateWithFormat:@self in %@, objectidarray]; Can I just clarify a couple of things about this: 1. The documentation says Important: If the receiver has not yet been saved, the object ID is a temporary value that will change when the object is saved. Now I want to use the IDs as keys in a dictionary, which of course copies the keys, and it is very likely that the objects will be added to the dictionary before saving, and referenced after save. I have checked, and the documentation is true Yes, the documentation is true :-) But when I try that, it throws an exception saying Can't resolve how to assign objects to stores; Coordinator does not have any stores. What is going on is that you haven't yet saved the NSDocument so there is no database backing it. Until the first save, the document is entirely in memory. We cannot assign a permanent objectID until you actually have a persistent store file. The documentation says Any object not already assigned to a store is assigned based on the same rules Core Data uses for assignment during a save operation (first writable store supporting the entity, and appropriate for the instance and its related items).. Yes. You can explicitly set the object to a store as well. My store class is registered in applicationWillFinishLaunching:, and everything seems to work OK while saving, so I would have expected it to be able to create one if it needed to, but the error seems to imply that I have to manually create my atomic store and add it to the coordinator before this, in order to be able to get it to generate the permanent ID. That's the store Class. The exception message is that your NSDocument's PSC doesn't actually have any stores (files) added to it at all. I just want to check that I haven't gone off on the wrong path again with this, and that I really do have to do all this to get a stable ID...it just seems to me that if I have to do all this just to get a stable ID, maybe there is some more fundamental design issue with my program, or concept I am not getting, and maybe I am trying to work against the way the framework is supposed to be used. Well, what are you doing such that you need this dictionary that maps object IDs before the document has ever been saved ? Where are you sending this dictionary off to ? 2. From the Predicate Programming Guide, it says that SELF refers to the objects that are being searched. I read through the whole guide and found nothing there stating that it could also refer to the managed object ID. I did eventually find it in the Core Data Programming Guide, but even when I knew what I was looking for, it took ages to find. I really think it should be mentioned in the Predicate Programming Guide - should I file a bug against that documentation? Please do. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Finding managed objects by URI representation
I have some queries that used to look up objects based on an elementID attribute, which used to be my unique identifier for objects, created when the objects were inserted or loaded. I am now moving away from that and using the standard managed object IDs and reference objects. So I used to do things like for a drag and drop of objects, I would have predicates like this (where the identifiers were the elementIDs of the objects I was dragging): NSPredicate *predicate = [NSPredicate predicateWithFormat:@%K in %@, @elementID, identifiers]; I guess that I will now have to use a different way of referencing it, and was thinking that I could create the identifiers as: [[[theManagedObject objectID] URIRepresentation] absoluteString] but if I was to do that, in order to search using a predicate, I would have to create a read only attribute on my managed objects that would return that value, right? No, this is going the wrong way. The objectID is the object's identity in the persistent store (e.g. primary key). You don't need to store pieces of it somewhere else. NSPredicate *predicate = [NSPredicate predicateWithFormat:@self == %@, myobjectid]; or NSPredicate *predicate = [NSPredicate predicateWithFormat:@self in %@, objectidarray]; I'm sort of looking at possible alternatives like using the MOCs persistentStoreCoordinator to get the managedObjectIDForURIRepresentation:, and then the MOC objectWithID: method to get the actual object back, but don't know if managedObjectIDForURIRepresentation: would handle the temporary ID situation and potential switch to a permanent ID on save. I see a few posts along these lines in the past, but I haven't specifically seen anything that says whether any of the methods will cope with looking up a temporary URI, if it has now been turned into a permanent one. If you stash a temporary URIRepresentation from an unsaved newly inserted object externally, like in the pasteboard, then no these methods won't track the switch to a permanent ID. Of course, passing around IDs to objects that don't exist in the persistent store yet is juggling fire. Those objects only exist in the MOC in which they were inserted until save, so nobody else can actually use their IDs yet. If you need a permanent ID immediately, you can call -obtainPermanentIDsForObjects:error: on the NSManagedObjectContext - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: iPhone CoreData TableView not updating
I did the first 7 chapters of More iPhone 3 Development, tackling iPhone SDK3 from Apress regarding the use of CoreData. In these 7 chapters a lot is explained and finally a series of classes and categories are being build that one can use as a Generic Controller for editing data stored in CoreData. So I bult a new application where I inserted these classes, and created a new viewController for my specific needs. Almost everything works, except that the tableView is not updated when I create a new item for in the DB, The item is however created, but only visible if I quit the App and start it again. Apparently something is not right. I double checked the 4 NSFetechedResult delegates, but it just does not work. I tried to insert [self.tableView reloadData] on a number of places, but to no avail. Have you tried starting a new project from the template ? That produces a completely functional Core Data application you can experiment with. I'm not familiar with the book, it's possible there's a bug in their sample code. If you're having problems with the NSFetchedResultsController, please note that the cache is *persistent* across application runs. The NSFetchRequest is a read only property. You should not mutate it. If you change the NSFetchRequest, NSPredicate (or the varargs), or NSSortDescriptor in any way, the cache will be invalid. In 3.1, this kinda usually worked anyway. In 3.2 and later, the caching is more aggressive and produces much faster launch times for FRC with complex string queries. This also means that buggy code that got away with it on 3.1 will no longer if it's recompiled against the 3.2 SDK (3.1 binaries still run fine) You may find the behavior more intuitive if you opt out with a nil cache name. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Invalidated managed objects
When I create a document, save it, then save as, then save as again, it duplicates the persistent store, so the managed objects I have been using in my application are all invalidated. Now there are a whole lot of places in my application where I have KVO set up on properties of the managed objects, and some places where I have the managed objects set as instance variables of my objects. I was not expecting them to be invalidated by the save as operation. They are not invalidated as part of Save. They are as part of Save As since that creates a new document, with a new persistent store, a new coordinator, new pretty much everything. Save As is creating a duplicate, so the original content still exists. However, the UI needs to be bound to the new content. Think of the behavior of TextEdit. Save As is like close old, duplicate, open new And created a method to refresh my data: - (void)managedObjectsChanged:(NSNotification *)notification { if ([[notification userInfo] objectForKey:NSInvalidatedObjectsKey]) { NSSet *objects = [[notification userInfo] objectForKey:NSInvalidatedObjectsKey]; for (NSManagedObject *obj in objects) { [[self managedObjectContext] refreshObject:obj mergeChanges:YES]; } } else if ([[notification userInfo] objectForKey:NSInvalidatedAllObjectsKey]) { for (NSManagedObject *obj in [[self managedObjectContext] registeredObjects]) { [[self managedObjectContext] refreshObject:obj mergeChanges:YES]; } } } I have confirmed that this is called in the save/save as/save as scenario, with the NSInvalidatedAllObjectsKey, and my object is getting sent the refreshObject message...But when I go to access my data, it still tells me that it has been invalidated. Am I doing something wrong here? Yes. You cannot resurrect invalidated objects. They are toast. That's why you're getting an exception. Nor would you want to in this case as they all represent content from the old (closed) document not the new saved as document. You need to toss all these references and then go through the regular I just opened a new document code path for your newly saved document to reconstruct the KVO observations. The canonical solution is to tell your controller objects like NSArrayController to refetch from the new document. It sounds like you're fighting the document based Save As behavior. It's close the existing document, save the current state to a new document, open new document - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: NSPredicate/NSExpression - can they solve this Core Data problem?
They are noted in the NSExpression.h header with API to create them and a comment to their functionality. - Ben Is the use of SUBQUERY() documented anywhere? The only mention I've seen of it is in the reserved keywords section of the Predicate Format String Syntax guide. Dave On Apr 2, 2010, at 3:42 PM, Ben Trumbull wrote: NSComparisonPredicate* exprPred = (NSComparisonPredicate*)[NSPredicate predicateWithFormat:@SUBQUERY(self, $key, %...@.$key != nil) == 0, obj]; NSExpression* expr = [exprPred leftExpression]; NSLog(@expression subquery results = %@, [expr expressionValueWithObject:keys context:nil]); ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Invalidated managed objects
On Apr 3, 2010, at 5:07 PM, Gideon King wrote: Wow, this is huge! Obviously the user doesn't expect the document to disappear and a new one open up just because they did a Save As operation I have I'm afraid led you astray. The invalidated objects are not coming from the Save As operation. The managed objects in the MOC bound to the NSPersistentDocument, and any other MOCs bound to that same PSC will keep the NSManagedObjects themselves in sync with the underlying document even as it is remapped to a new file. I'll send you a sample project from the template that logs the notifications from NSManagedObjectContext and NSPersistentStoreCoordinator. The NSManagedObject pointers remain undisturbed. 2010-04-03 18:13:41.110 testDocs[14260:a0f] PSC changed: { changedUUID = ( NSSQLCore: 0x1025513e0, NSSQLCore: 0x103051890, ( 0x1025a4bc0 x-coredata://7401BACF-50F9-4699-8B65-520963A9B084/Entity/p3, 0x103055bc0 x-coredata://9D960070-9CAE-4BDD-9EDF-99ACCD83AF7F/Entity/p1, 0x102504fb0 x-coredata://7401BACF-50F9-4699-8B65-520963A9B084/Entity/p2, 0x103051d40 x-coredata://9D960070-9CAE-4BDD-9EDF-99ACCD83AF7F/Entity/p3, 0x102598810 x-coredata://7401BACF-50F9-4699-8B65-520963A9B084/Entity/p1, 0x1004eeb10 x-coredata://9D960070-9CAE-4BDD-9EDF-99ACCD83AF7F/Entity/p2 ) ); } 2010-04-03 18:13:41.112 testDocs[14260:a0f] PSC changed. Registered objects = {( NSManagedObject: 0x1004db090 (entity: Entity; id: 0x103051d40 x-coredata://9D960070-9CAE-4BDD-9EDF-99ACCD83AF7F/Entity/p3 ; data: { name = baz; }), NSManagedObject: 0x1004db120 (entity: Entity; id: 0x103055bc0 x-coredata://9D960070-9CAE-4BDD-9EDF-99ACCD83AF7F/Entity/p1 ; data: { name = foo; }), NSManagedObject: 0x1004daea0 (entity: Entity; id: 0x1004eeb10 x-coredata://9D960070-9CAE-4BDD-9EDF-99ACCD83AF7F/Entity/p2 ; data: { name = zap; }) )} 2010-04-03 18:13:53.604 testDocs[14260:a0f] PSC changed: { changedUUID = ( NSSQLCore: 0x103051890, NSSQLCore: 0x103062860, ( 0x10305b680 x-coredata://9D960070-9CAE-4BDD-9EDF-99ACCD83AF7F/Entity/p1, 0x10306a890 x-coredata://381F89A2-9204-45E8-850B-1CED8022E32E/Entity/p1, 0x10305b3c0 x-coredata://9D960070-9CAE-4BDD-9EDF-99ACCD83AF7F/Entity/p2, 0x10306aa80 x-coredata://381F89A2-9204-45E8-850B-1CED8022E32E/Entity/p2, 0x1030681e0 x-coredata://9D960070-9CAE-4BDD-9EDF-99ACCD83AF7F/Entity/p3, 0x10306ae20 x-coredata://381F89A2-9204-45E8-850B-1CED8022E32E/Entity/p3 ) ); } 2010-04-03 18:13:53.604 testDocs[14260:a0f] PSC changed. Registered objects = {( NSManagedObject: 0x1004db090 (entity: Entity; id: 0x10306ae20 x-coredata://381F89A2-9204-45E8-850B-1CED8022E32E/Entity/p3 ; data: { name = baz; }), NSManagedObject: 0x1004db120 (entity: Entity; id: 0x10306a890 x-coredata://381F89A2-9204-45E8-850B-1CED8022E32E/Entity/p1 ; data: { name = foo; }), NSManagedObject: 0x1004daea0 (entity: Entity; id: 0x10306aa80 x-coredata://381F89A2-9204-45E8-850B-1CED8022E32E/Entity/p2 ; data: { name = zap; }) )} There are no invalidate managed objects if you create a Core Data document based app from the Xcode template and then perform Save As. I'll take another look at your sample project. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Invalidated managed objects
On Apr 3, 2010, at 6:25 PM, Gideon King wrote: Phew, that's a relief. I'll look forward to hearing what I'm doing wrong in my sample project then. Regards Gideon On 04/04/2010, at 11:22 AM, Ben Trumbull wrote: On Apr 3, 2010, at 5:07 PM, Gideon King wrote: Wow, this is huge! Obviously the user doesn't expect the document to disappear and a new one open up just because they did a Save As operation I have I'm afraid led you astray. The invalidated objects are not coming from the Save As operation. The managed objects in the MOC bound to the NSPersistentDocument, and any other MOCs bound to that same PSC will keep the NSManagedObjects themselves in sync with the underlying document even as it is remapped to a new file. I'll send you a sample project from the template that logs the notifications from NSManagedObjectContext and NSPersistentStoreCoordinator. The NSManagedObject pointers remain undisturbed. Okay. The invalidations are coming from the PSC when it removes the old store. At this point it's already added the new store, and it's already told the MOC to switch all the objectIDs from its MOs to point to the new store. The problem is that your custom store wasn't being honest about the objectIDs, it wasn't even saving them at all, so the remapping between the old and new store fails. At that point, the MOC still has objects from the old store in it, because the new store handed out random IDs and the mapping failed to find them. So I changed your save method to write out both the UUID and the string data so your store could properly reconstruct the cache node it loads. There was another subtle issue from your reusing the metadata dictionary to hold your row data. The problem is you set the metadata at unfortunate times, both before and after the store is created. Instead of changing it in -init (too soon) and in -load (too late), it works better to use -loadMetadata. This is a problem for you since you were overwriting the in memory metadata with the plist you wrote to disk which effectively caused the store to change it's UUID. The documentation isn't clear on this, so you shouldn't feel bad about missing this part. You also had -metadata spontaneously mutate the row data, which is bad. It's really important for stores to keep their UUID, and for them to hand back the same objectID for an object that they did when we asked before. Otherwise constructing a map from old ids to new ids and looking up stores by their UUID is kinda hard. I'll send you back the original and edited sample projects so you can diff them. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Invalidated managed objects
On Apr 3, 2010, at 9:44 PM, Gideon King wrote: Excellent, thanks for that. I thought that once a managed object ID had been assigned, that newReferenceObjectForManagedObject: should always return the same value, so I was regenerating it from the previous data instead of generating a new one if it either had a temporary object id or no object id. I will switch over my code to do this. Uhm, that's not exactly what I said. It should look something like: - (id)newReferenceObjectForManagedObject:(NSManagedObject *)managedObject { id result = nil; NSManagedObjectID* oid = [managedObject objectID]; if ((oid == nil) || [oid isTemporaryID]) { CFUUIDRef uuidRef = CFUUIDCreate(NULL); CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef); result = [[NSString alloc] initWithString:(NSString*)uuidStringRef]; CFRelease(uuidStringRef); CFRelease(uuidRef); } else { result = [[self referenceObjectForObjectID:oid] retain]; } return result; } although all the Core Data stores use a single 64 bit integer with the current seed value stored in the metadata instead of UUIDs. In my current implementation, I have a base class for all my managed objects that has an id string (elementID) set in it. The only times it is persisted is for objects that I need to refer to in relationships. This means I can regenerate the value from the managed objects if necessary, so my new method will look like this: The objectID value (referenceObject) needs to be the same every time you load the document. If you open the document multiple times, and give the same objects different objectIDs, badness happens. For Save As, we're generating a dictionary of old IDs - new IDs. When we add the new store to the coordinator, if the store hands back random values for the migrated objects, then the MOC can't fix its MOs to point to the new store's cache nodes. if you: (1) open the document (2) fetch all the objects into array 'results' (with a sort ordering) (3) NSLog(@results = %@, [results valueForKey:@objectID]), (4) close the document (5) repeat steps 1-4 (6) compare the 2 logs If the strings don't match, you're SOL. There is only one area of my code where this will cause an issue, where I pre-generate some of the xml for my file using some references to some managed objects. I'm sure I'll be able to find a way around this though. Hopefully that will resolve that issue. Also, I had not noticed the loadMetadata: method, so was just creating it in the init for new files, or in my load method for existing ones. I will have to do a bit of work to change that to load using loadMetadata: since I will have to extract the file from a zip file separately from the main load method which already unzips to a temporary folder. Not a biggie though. You don't have to use it, but for the sample project you sent that conflated the metadata and raw data, it was the best way to avoid nuking the already loaded metadata when you loaded the raw data. It's probably all around better though. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: NSPredicate/NSExpression - can they solve this Core Data problem?
Objects: - NSManagedObject *item - some managaged object - NSArray *attributes - an array of the item's attributes Desired Result: - a possibly smaller array of attribites where [item valueForKey:an attribute] != nil. In code, I can simply iterate over the keys, perform the valueForKey, and skip nil values. I played around with expressions and predicates to solve this, to no avail. Having the array of attributes unrolled separately is a little odd. Do you mean you have an array of attribute names from, say the entity, and you want to ask a MO for all its non-nil attribute values and get back an array of matching attribute names for those non-nil values? - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: NSPredicate/NSExpression - can they solve this Core Data problem?
On Apr 2, 2010, at 1:30 PM, David Hoerl wrote: Having the array of attributes unrolled separately is a little odd. Do you mean you have an array of attribute names from, say the entity, and you want to ask a MO for all its non-nil attribute values and get back an array of matching attribute names for those non-nil values? Specifically, here is what I'm trying to do: I have a NSManagedObject Address (and no treecontroller) that has properties - lets say name, homePhone, and workPhone. workPhone is optional. So, one Address record has obj.name = Joe Blow, obj.homePhone = 555-1212 and obj.workPhone = nil I put the Address object's property keys in an NSArray *attributes = { @name, @homePhone, @workPhone ) The exercise is to reduce the attribute array to just the set of keys that when applied to the object result in a value - that is, not nil. Two approaches: NSDictionary* obj = [NSDictionary dictionaryWithObjectsAndKeys:@Joe Blow, @name, @555-1212, @homePhone, nil]; NSArray* keys = [NSArray arrayWithObjects:@name, @homePhone, @workPhone, nil]; NSPredicate* pred = [NSPredicate predicateWithFormat:@%...@.self != nil, obj]; NSLog(@filtered array results = %@, [keys filteredArrayUsingPredicate:pred]); NSComparisonPredicate* exprPred = (NSComparisonPredicate*)[NSPredicate predicateWithFormat:@SUBQUERY(self, $key, %...@.$key != nil) == 0, obj]; NSExpression* expr = [exprPred leftExpression]; NSLog(@expression subquery results = %@, [expr expressionValueWithObject:keys context:nil]); - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Core Data doesn't save toMany relations please HELP :S
So was digging more into the problem, and realize that the Items are being saved the ItemXInvoice are being saved and related to the invoice, but I can't acces the invoice detail (ItemXInvoice) immediately I get a console error: The array controller doesn't appreciate what you're doing to it. Is there a reason you haven't just configured it in Entity mode with an Entity name and a Managed Object Context binding ? Then the array controller will just listen to the NSManagedObjectContext notifications and do most of this for you. Cannot remove an observer NSTableBinder 0x116087000 for the key path toItem.descr from ItemXInvoice 0x116024800, most likely because the value for the key toItem has changed without an appropriate KVO notification being sent. Check the KVO-compliance of the ItemXInvoice class. Somewhere you've changed the toItem property without going through the setter method properly. I then checked the class and in the setItem Im doing: -(void)setToItem:(Item *)value{ [self willChangeValueForKey:@toItem]; [value retain]; [self setPrimitiveValue:value forKey:@toItem]; [self didChangeValueForKey:@toItem]; } Where'd you get this ? It's not from any of the Xcode templates, and it's wrong (leaks). There is no need to retain value, and since this setter doesn't do anything interesting, you should just delete it and use the @dynamic property from the Xcode Design menu - Data Modeling - Copy Objective-C 2.0 Method Declarations to Clipboard -(IBAction)addItemXInvoice:(id)sender{ Item * newItem = [NSEntityDescription insertNewObjectForEntityForName:@Item inManagedObjectContext:[self managedObjectContext]]; ItemXInvoice * newItemXInvoice = [NSEntityDescription insertNewObjectForEntityForName:@ItemXInvoice inManagedObjectContext: [self managedObjectContext]]; [newItemXInvoice willChangeValueForKey:@toItem]; [newItemXInvoice setValue:newItem forKey:@toItem]; [newItemXInvoice didChangeValueForKey:@toItem]; [_itemsArrayController addObject:newItemXInvoice]; if(_newInvoice == nil){ _newInvoice = [NSEntityDescription insertNewObjectForEntityForName:@Invoice inManagedObjectContext:[self managedObjectContext]]; } [_newInvoice addToItemsXInvoiceObject:newItemXInvoice]; [_tempItemsArray addObject:newItem]; //I need to keep track of the newly created itmes by this method so I can safelty remove them. //Set the creationItemPrice for each ItemXInvoice } uhm. lots of not healthy things here. You shouldn't be calling -willChange/-didChange explicitly here. That's the purpose of the setter methods (or KVC). Not sure why a bunch of this code isn't in -awakeFromInsert. Or why you have _tempItemsArray at all. As others have noted, Cocoa Bindings isn't your Model layer and trying to use it that way is both tedious and error prone. It's the Controller layer that intermediates between your Model objects and the UI. You really want to focus these kinds object graph (data management) operations on the Core Data objects, and leverage those APIs along with KVO and NSNotification. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: NSSortDescriptor and Block
while experimenting with sorting methods I got a strange error message with -sortDescriptorWithKey:ascending:comparator:. In the following listing (as simple as possible) an Array can be sorted with -sortedArrayUsingDescriptors: with a selector, and -sortedArrayUsingComparator: But -sortedArrayUsingDescriptors: with a comparator results in Error: -[NSCFNumber rx]: unrecognized selector. So the method expects the key rx not in my class as it should but in the NSNumber class. A bug? Or is something wrong in my approach? The arguments to the Block with the NSSortDescriptor are the results of calling valueForKeyPath, not the original objects in the array. NSSortDescriptor has already pulled the values for the keypaths out and caches them. So your using the key @rx with that block is like: [[obj1 valueForKeyPath:@rx] rx] If you don't want NSSortDescriptors KVC functionality, you can just use -sortedArrayUsingComparator:. You can also try a nil keypath, although I'm not certain that works. - Ben // Sort array with -sortDescriptorWithKey:ascending:comparator: // Error: -[NSCFNumber rx]: unrecognized selector NSSortDescriptor *descr3 = [ NSSortDescriptor sortDescriptorWithKey:@rx ascending:YES comparator:^NSComparisonResult(id obj1, id obj2) { if ([obj1 rx] [obj2 rx]) { return NSOrderedAscending; } else if ([obj1 rx] [obj2 rx]) { return NSOrderedDescending; } return NSOrderedSame; }]; ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: NSSortDescriptor and Block
Well, if you need a nil keypath you probably shouldn't be using sort descriptors. The primary value of sort descriptors are (a) the KVC caching and (b) an OO representation of a sort criteria. The standard -sortedArrayUsingComparator: is for blocks like the one you wrote that are completely free form. - Ben On Mar 24, 2010, at 3:55 AM, Jochen Moeller wrote: Hello Ben, thanks very much for your answer. Your idea with the nil keypath works! So changing the line in question to: NSSortDescriptor *descr3 = [ NSSortDescriptor sortDescriptorWithKey:nil ascending:YES comparator:^NSComparisonResult(id obj1, id obj2) { is sorting the array without claims. The syntax of that method is a bit confusing. When using a selector the key is required, and when using a comparator it is not. Thanks again, Jochen Moeller Am 24.03.2010 um 11:12 schrieb Ben Trumbull: while experimenting with sorting methods I got a strange error message with -sortDescriptorWithKey:ascending:comparator:. In the following listing (as simple as possible) an Array can be sorted with -sortedArrayUsingDescriptors: with a selector, and -sortedArrayUsingComparator: But -sortedArrayUsingDescriptors: with a comparator results in Error: -[NSCFNumber rx]: unrecognized selector. So the method expects the key rx not in my class as it should but in the NSNumber class. A bug? Or is something wrong in my approach? The arguments to the Block with the NSSortDescriptor are the results of calling valueForKeyPath, not the original objects in the array. NSSortDescriptor has already pulled the values for the keypaths out and caches them. So your using the key @rx with that block is like: [[obj1 valueForKeyPath:@rx] rx] If you don't want NSSortDescriptors KVC functionality, you can just use -sortedArrayUsingComparator:. You can also try a nil keypath, although I'm not certain that works. - Ben // Sort array with -sortDescriptorWithKey:ascending:comparator: // Error: -[NSCFNumber rx]: unrecognized selector NSSortDescriptor *descr3 = [ NSSortDescriptor sortDescriptorWithKey:@rx ascending:YES comparator:^NSComparisonResult(id obj1, id obj2) { if ([obj1 rx] [obj2 rx]) { return NSOrderedAscending; } else if ([obj1 rx] [obj2 rx]) { return NSOrderedDescending; } return NSOrderedSame; }]; - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Merge changes between two Managed Object Contexts
If you saved the temporary MOC, then all you have to do is call -[NSManagedObjectContext mergeChangesFromContextDidSaveNotification:] on the main MOC with the save notification from the temporary MOC. It's moving unsaved changes around that's cumbersome. - Ben Thank you Joanna, that was very helpful. So, from your experience, mergeChangesFromContextDidSaveNotification: will not pick up adding a new object, just editing an existing one? It seems like the real pain point is having to re-create the new object in the second managedObjectContext and copy all it's properties. Austin On Mar 21, 2010, at 6:48 PM, Joanna Carter wrote: Hi Austin I've got a Core Data project and I have a window controlled by an NSWindowController that is used to add or edit an NSManagedObject. I wanted to create a separate Managed Object Context in the NSWindowController so that the changes made to the object aren't seen by the UI below and so I can just throw away the changes if the user clicks Cancel. If the user clicks Save' I wanted to merge the changes from the temporary MOC to the main MOC being used by the rest of the application. What is the best way to merge those changes back to the main MOC? Do I have to recreate the object in the main MOC as is done in this example: http://www.timisted.net/blog/archive/multiple-managed-object-contexts-with-core-data? Or, can I use - (void)refreshObject:(NSManagedObject *)object mergeChanges:(BOOL)flag to merge the changes? If i use refreshObject:mergeChanges, can I pass it my NSManagedObject from the temporary MOC and do I need to call save: on the temporary MOC before doing that? ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: CoreData database sharing and migration
On Mar 22, 2010, at 4:06 AM, Steve Steinitz wrote: On 18/3/10, Ben Trumbull wrote: there wasn't a good solution for multiple machines simultaneously accessing an SQLite db file (or most other incrementally updated binary file formats). By good I mean a solution that worked reliably and didn't make other more important things work less well. I'm curious about the reliability issues you saw. Also, by less well do you mean slower? Because the different NFS clients have file caches with differing amounts of staleness, and the SQLite db is updated incrementally, it's possible for an NFS client to think it has the latest blocks, and then derive material from one and write it into another (it is after all a b-tree). The written blocks have implicit dependencies on all the other active blocks in the database, so having stale data is bad. For nearly all Mac OS X customers (sadly not you) achieving a near 100x performance boost when accessing database files on an AFP or SMB mount (like their home directory in educational deployments) is pretty huge. I agree. But wouldn't those same educational institutions be prime candidates for multiple machine access? No. The restriction is multiple physical machines using the same database files simultaneously (open). While AFP will allow multiple logins to the same account when configured with an advanced setting, in general, AFP servers actually prevent users from multiple simultaneous logins to the same account. To address both sets of problems on all network FS, we enforce a single exclusive lock on the server for the duration the application has the database open. Closing the database connection (or logging out) allows another machine to take its turn. Could my application close the database connection and re-open it to work around the problem? How would I do that? I suppose once I got it going I'd have to retry saves, fetches etc. In theory, one could, but in practice that won't be very manageable without architectural changes. Something along the lines of open the remote connection, pull down interesting data and save it to a local file, and close the remote connection. Given your current deployment set up provides adequate performance, and all you need is a bug fix, I'm not sure this would be very helpful. You'll get the 10.5 performance characteristics, however. Again, the 10.5 performance over gigabit ethernet is almost unbelievably fast. I may know why. Despite your helpful explanations I'm still not exactly clear on the relationship between caching and locking, but I wonder if the speed we are seeing is helped by the fact that the entire database fits into the Synology NAS's 128meg cache? That probably doesn't hurt. In another message in this thread, you made a tantalizing statement: Each machine can have its own database and they can share their results with NSDistributedNotification or some other IPC/networking protocol. You can hook into the NSManagedObjectContextDidSaveNotification to track when one of the peers has committed changes locally. Let me guess how that would work: before saving, the peer would create a notification containing the inserted, updated and deleted objects as recorded in the MOC. The receiving machine would attempt to make those changes on its own database. Some questions: Would that really be feasible? yes, but as you observe, it's more tractable for simple data records than complex graphs with common merge conflicts Would it be a problem that each machine would have having different primary keys? yes How would the receiving machine identify the local objects that changed remotely? typically this is done with a UUID a fetch. Since each database on each client is different, the stores themselves have different UUIDs, and any encoded NSManagedObjectID URI will note which store the objectID came from, so you could also map them the objectID URIs to the local value directly. Could relationships (indeed the object graph itself) feasibly be maintained? yes. Updates to to-many relationships require the use of a (additions, subtractions) pair instead of simply setting the new contents. How would relationships between the remote objects be identified? Hand-parsing? Either by UUID or objectID URI. Has anyone done it that you know of? yes. I'm aware of 4 solutions, however, I would only recommend 1 as appropriate for the general (skill, time, pain threshold) and it avoids complex relationship graphs. Basic data record replication over DO. Is there sample code? no. The only real trick in converting the didSave notification into something appropriate for DO to consume is to copy it and replace the NSManagedObjects with a dictionary that has a UUID instead of an object ID, the attribute values, and the relationship contents expressed as a list of UUIDs. - Ben
Re: CoreData database sharing and migration
I'm not sure I understand your question. How is they just use Core Data with our SQLite NSPersistentStore and it works for multiple processes on a single machine. For most but not all customers, that's coordinated with POSIX byte range advisory locks (fcntl). Why is to have Cocoa frameworks vending the user's Contacts and Events to various apps through an API. Mail, iCal, and Address Book may all be working with Contacts data simultaneously. etc. - Ben On Mar 19, 2010, at 12:42 PM, Aurélien Hugelé wrote: Hi Ben, Can you be kind enough to explain how Apple frameworks do that ? (multiple processes, single machine, one SQLite database) I'm thinking of CalendarStore, AddressBook framework. Can you describe what is the general idea behind those Apple frameworks? Aurélien, Objective Decision Team On 17 mars 2010, at 22:29, Ben Trumbull wrote: I am wondering whether it is possible to create a database in core data that can be opened by more than one application at the same time. It is currently impossible to handle one SQLite database with two instances of the same app. The problem is if user1 quits the app, the data is saved but user2's instance of the app doesn't recognize this file system change and just overwrites its version in memory. So the data from user1 is gone. Is there a way I can handle this? Second -- I am having more than two database versions now but still want to support my version 1.0 but the mapping model only allows one source model as well as only one target model. I would have to remove one version but that makes version 1.0 users' database unusable. Has anyone gotten something like this to work? Yes, several Apple frameworks use Core Data databases from multiple processes simultaneously with a single user account and single physical machine. Do you mean more than one application simultaneously on more than one physical computer over NFS/AFP/SMB ? Don't do that. Or do you mean an NSDocument based application using Core Data an SQLite store ? NSDocuments intentionally behave like TextEdit. Last writer wins, overwites everything. If so, you should be using a non-document based Core Data project template. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/hugele.aurelien%40objective-decision.com This email sent to hugele.aurel...@objective-decision.com - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Checking integrity of a Core Data document with SQLite store
The integrity check will do that, but there's no way to mitigate the I/O costs. Conceptually, if you added a simple checksum, you'd still have to read the entire file to verify it matched the checksum. This file corruption should be extremely rare, and should only occur after power loss or kernel panic or a user hitting the power button for an unclean reboot. If not, please file a bug. If so, but you have data you are willing to share with us, also please file a bug. - Ben On Mar 17, 2010, at 10:04 PM, Dave Fernandes wrote: Thanks, Ben and mmalc for the responses. I found that prefetching the commonly accessed objects in my document's initWithContentsOfURL:ofType:error method finds the corruption pretty reliably. Hopefully, this is a reasonably robust check without being as i/o intensive as an integrity check. It would be nice, though, to have a method that I was sure provided good coverage of the physical file. Dave On 2010-03-15, at 11:03 PM, Ben Trumbull wrote: On Mar 15, 2010, at 7:49 PM, Dave Fernandes wrote: On 2010-03-15, at 3:30 PM, Ben Trumbull wrote: Running an integrity check can be useful if you have previously gotten a corrupt db error back from fetching or saving, or your app previously crashed, or you have some other active indicator it might be worthwhile. However, it's quiet expensive in I/O and you should not do it on every app launch / document open. Customers with account home directories on AFP, NFS or SMB servers will be very unhappy, and if your files become large enough so will people using local drives. That's a shame. It would certainly be easier to check when opening the file than to scatter code all over the app to diagnose the problem. Is there a standard exception I can expect to get when fetching or saving? NSInternalInconsistencyException is the one I currently get. You should generally get an NSError with the code that is NSFileReadCorruptFileError. In gdb, what does: future-break objc_exception_throw future-break +[NSError errorWithDomain:code:userInfo:] ... info threads thread apply all bt say ? The only place you should get an exception is if you try to fault in an object that is unreachable. Either because the database was deleted or corrupted, or because another thread / context deleted that object you wanted before you and you only held the reference instead of getting all the data at once (e.g. race on delete) - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: CoreData database sharing and migration
On 17/3/10, cocoa-dev-requ...@lists.apple.com wrote: Do you mean more than one application simultaneously on more than one physical computer over NFS/AFP/SMB ? Don't do that. When did that become the official policy, Ben? The short answer is in 10.6. The longer answer is that there are two classes of problems, one for NFS and one for AFP/SMB. For NFS, the protocol does not reliably allow for clients to maintain their local file caches coherently with the server in real time. Only the newest NFS servers even respect file cache coherency around byte range file locks **at all**. Unfortunately, the latest protocol doesn't mesh well with SQLite or most other existing incrementally updated file formats. Many deployed NFS servers and clients only provide on close to open coherency. File modification timestamps on NFS severs may (or may not) provide accuracy better than 1.0 seconds. And so forth. There's also no good way to perform a cache bypassing file read on OSX or selectively evict ranges of files from the local file cache by hand. We churned on this for a while with various teams, and there wasn't a good solution for multiple machines simultaneously accessing an SQLite db file (or most other incrementally updated binary file formats). By good I mean a solution that worked reliably and didn't make other more important things work less well. NFS is just not a free Oracle. Software that wants real time distributed cache coherency needs to use IPC and mange the problem themselves. It is trivial to write a program that writes to a file on NFS and sends the same update to its clone on another machine via IPC and for its clone to verify that NFS cache coherency indeed fails regularly (e.g. file read bytes != IPC read bytes). This is what I mean by real time distributed cache coherency. For AFP SMB, the problem is different. These FS do not support POSIX advisory byte range locks at all. They only support mandatory locks. Consequently, they never cache data read from files with any existing locks at all. No file caching means all the I/O is slow. Painfully slow. AFP over Airport without file caching is bad. The I/O throughput on a file free of locks on AFP is close to 100x better than a file with a single byte locked that isn't even anywhere near where you are reading. For nearly all Mac OS X customers (sadly not you) achieving a near 100x performance boost when accessing database files on an AFP or SMB mount (like their home directory in educational deployments) is pretty huge. So we focused on making the experience that could work well work even better. 10.6 is a significantly better network FS client as Apple applications like Mail don't slam the servers with byte range lock requests all the time (good for NFS), and on AFP also gets to use local file caching. To address both sets of problems on all network FS, we enforce a single exclusive lock on the server for the duration the application has the database open. Closing the database connection (or logging out) allows another machine to take its turn. This behavior was supposed to be opt in for Core Data apps, but on 10.6.2 is not. I'm doing that with some success. For the past three years, my client's point of sale system has been connecting from five machines to a single database on a Synology network drive over afp. I had to write some code to periodically get the latest values from the database during idle time. That was a little complicated but its working well now. It can work technically on AFP. However, the distributed cache coherency problem is avoided by these network FS because they don't do any file caching on files with locks. Your server set up and networking hardware is pretty sophisticated compared to most so the performance is adequate. As an engineer, I would wish AFP over VPN over Airport was the more uncommon deployment scenario, but sadly not. There are mysterious but harmless optimistic locking errors once in a while where no attributes appear have changed -- just to keep me on my toes, along with an occasional real data collision (two staff members carelessly editing the same object) but we've had no real issues in a year or so. Those mysterious updates are probably version numbers being bumped because the object's participation in a to-many relationship, either outgoing or incoming, changed. However, 10.6.2 has a bug where only one machine can connect to a Core Data store (its likely a sqlite-level connection issue -- but I'm not sure). ADC did pass your bug report along to us, and it is a backward binary compatibility issue, and a regression from 10.6.0. It will be fixed in a future update. You'll get the 10.5 performance characteristics, however. So, for a while we were down to one machine. I eventually had to roll the five machines back to Leopard. That remains a PR nightmare. I'm sorry about that.
re: Multiple validation errors. Why?
I have a core data application. One entity is Hit. This entity typically has no instances when the application starts, and gets populated during the execution of the program. All of the instances appear to be OK when viewed in a TableView. Some or all instances may get deleted before the application quits. If any instances remain when the app quits, I get a multiple validation errors occured message. The console has no error messages. In debugging, I have gone so far as supplying no data at all when adding any instances, so they get created with default values only. Still I get the error message. Some attributes are strings, some are 16 bit integers, and some are 32 bit integers. The string defaults are empty strings, and the integer defaults are zero. I am creating Hit instances with this method The NSError has an -userInfo dictionary with at least some answers to your questions in it. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: User-space threads and NSAutoreleasePool
The problem is that when you call swapcontext() to switch the user-thread running on a kernel-thread, the NSAutoreleasePool stack is not swapped out. It remains rooted in thread-local storage. As a result, serious problems result. Let me give an example. - (void)doStuff { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; // do some stuff that calls swapcontext() [pool drain]; } That doesn't work correctly with regular threads, pthreads, GCD or any other concurrency pattern. The autorelease pool needs to be pushed and popped within the context of the asynchronous subtask. The original thread of control needs its own pool, and you cannot rely upon autorelease to keep objects alive across asynchronous task boundaries. You will need to be careful to ensure that the sending thread transfers ownership of a retain to the receiving thread which releases it. Not autoreleases it. It would need to conceptually be: - (void)doStuff { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; // some stuff Before [pool drain]; // do some stuff that calls swapcontext(), and has it's only scoped autorelease pool pool = [[NSAutoreleasePool alloc] init]; // some more stuff After [pool drain]; } Object you want to keep alive across drains should be retained and later released. Autorelease pools are cheap. Make more. A lot more. If you have places where that doesn't work with coroutines then don't leak important objects into autorelease pools. Either don't create them that way, or retain them again, and release them after you can verify the autorelease pool was destroyed. Using kernel-level threads is, naturally, simpler, but due to the cost of kernel-level context switches, they don't scale well. (You just can't run 500 threads at once very well.) User-space threads require more programmer attention, certainly, but also allow certain programming models that can't be done otherwise. For example, they can be used for coroutines, cooperative threading, or all sorts of esoteric-yet-sometimes-useful effects. That's true, but I'm not sure it's relevant for the I wish I had 500 threads to handle this web server communication task. There are a lot of different ways to organize the tasks as inert data that can be processed cooperatively by a set of active worker threads. The mental model of each task being its own little world and a thread is nice, but it's also artificial. You can reorganize the relationship between active workers and command data. Nothing is stopping you from that except a preconception that tasks and threads must have a 1:1 correspondence. You could create a finite state machine to process a queue of the tasks as inert command structures and firing up 8 of those finite state machines on their own dedicated normal threads on your Mac Pro. The semantic effect will be similar to user threads, yet you won't run into all these crazy problems. Destroying thread local storage and its affect on autorelease pools is just your first problem stuffing Cocoa into swapcontext() This point from Mike is particularly apt: Since you mention in another message that this is portable, cross-platform code, why do you need to call Cocoa from the user-level threads at all? Separate your code into cross-platform code that does this swapcontext stuff, and Mac-specific code that doesn't, and you should be good. You could use a sandboxing approach like Safari and have your cross platform code run in a background CLI and talk to the GUI app over IPC. No Cocoa in the CLI and no user threads in the GUI app. Pretty sure, though, you'd get better performance conceptually restructuring the task / actor relationship. The cooperative finite state machine approach along with a prioritized queue can provide soft real time performance with thousands of task objects ... every second. That's basically what some older MMORPG servers did before people went wild with distributed computing. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: NSManagedObject awakeFromFetch not sent on secondary thread?
However I still don't understand why awakeFromFetch is not sent in secondary thread - I can't find any clue in documentation? Is this a bug or is it a feature? I'm running on 10.6.2. Did you try setting the NSFetchRequest option -setReturnsObjectsAsFaults:NO ? The default setting will attempt to lazily copy the fetch results into the managed objects as much as possible (the data is still fetched eagerly from the database by -executeFetchRequest: as you can see with SQL logging) Perhaps you are always touching the objects on the main thread eagerly so there is no apparent difference there. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: CoreData database sharing and migration
I am wondering whether it is possible to create a database in core data that can be opened by more than one application at the same time. It is currently impossible to handle one SQLite database with two instances of the same app. The problem is if user1 quits the app, the data is saved but user2's instance of the app doesn't recognize this file system change and just overwrites its version in memory. So the data from user1 is gone. Is there a way I can handle this? Second -- I am having more than two database versions now but still want to support my version 1.0 but the mapping model only allows one source model as well as only one target model. I would have to remove one version but that makes version 1.0 users' database unusable. Has anyone gotten something like this to work? Yes, several Apple frameworks use Core Data databases from multiple processes simultaneously with a single user account and single physical machine. Do you mean more than one application simultaneously on more than one physical computer over NFS/AFP/SMB ? Don't do that. Or do you mean an NSDocument based application using Core Data an SQLite store ? NSDocuments intentionally behave like TextEdit. Last writer wins, overwites everything. If so, you should be using a non-document based Core Data project template. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: CoreData database sharing and migration
On Mar 17, 2010, at 2:59 PM, Tobias Jordan wrote: Hello Ben, Thanks a lot for responding! My problem is as follows: The database which is currently a non-document based core data SQLite one is normally stored in the local User Library of the user. (/Users/user/Library/Application Support/MyApp/database.db But there are cases in which two (or more) different physical machines must have access to the database. Don't do that. Network file systems do not provide real time distributed cache coherency. NFS is not a free version of Oracle. For example two designers working on a project and they both need the same database so they can share their results. This means they create a new database on their server and link my app to this database. Each machine can have its own database and they can share their results with NSDistributedNotification or some other IPC/networking protocol. You can hook into the NSManagedObjectContextDidSaveNotification to track when one of the peers has committed changes locally. - Ben As you've said, is there a way the data can be always immediately written to disk so there's no 'last writer wins'? I am not using NSDocument based techniques -- it is really just one core data DB. Thank you! Regards, Tobias On Mar 17, 2010, at 10:29 PM, Ben Trumbull wrote: I am wondering whether it is possible to create a database in core data that can be opened by more than one application at the same time. It is currently impossible to handle one SQLite database with two instances of the same app. The problem is if user1 quits the app, the data is saved but user2's instance of the app doesn't recognize this file system change and just overwrites its version in memory. So the data from user1 is gone. Is there a way I can handle this? Second -- I am having more than two database versions now but still want to support my version 1.0 but the mapping model only allows one source model as well as only one target model. I would have to remove one version but that makes version 1.0 users' database unusable. Has anyone gotten something like this to work? Yes, several Apple frameworks use Core Data databases from multiple processes simultaneously with a single user account and single physical machine. Do you mean more than one application simultaneously on more than one physical computer over NFS/AFP/SMB ? Don't do that. Or do you mean an NSDocument based application using Core Data an SQLite store ? NSDocuments intentionally behave like TextEdit. Last writer wins, overwites everything. If so, you should be using a non-document based Core Data project template. - Ben - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: CoreData database sharing and migration
About the second question -- do you know how to solve the migration with more than two data models? From a specific NSPersistentStore's perspective there is only ever 1 data model. When you use multiple models and merge them together, then *union* is the data model. The object you pass to the NSPersistentStoreCoordinator initializer is the one true data model regardless of whether it only exists in memory or how many separate model files were used to compose it. When you migrate a store that is built on the union of multiple models, you must migrate the union to a new union. You cannot migrate things piecemeal. However, as you can merge models together at runtime, so too could you construct mapping models programmatically to handle the union. However, that's probably more trouble than it's worth. You should focus on migrating the union model that was used with the store as its own entity. It might be easier to think about how you would give each schema a version number. Each union of models is likely to be its own version, and you would migrate to a new union with a new version number. - Ben On Mar 17, 2010, at 3:28 PM, Tobias Jordan wrote: Hi Ben, Thanks so much for this brilliant suggestion, I haven't thought about something like this before but it's actually really fantastic. About the second question -- do you know how to solve the migration with more than two data models? - Tobias On Mar 17, 2010, at 11:13 PM, Ben Trumbull wrote: On Mar 17, 2010, at 2:59 PM, Tobias Jordan wrote: Hello Ben, Thanks a lot for responding! My problem is as follows: The database which is currently a non-document based core data SQLite one is normally stored in the local User Library of the user. (/Users/user/Library/Application Support/MyApp/database.db But there are cases in which two (or more) different physical machines must have access to the database. Don't do that. Network file systems do not provide real time distributed cache coherency. NFS is not a free version of Oracle. For example two designers working on a project and they both need the same database so they can share their results. This means they create a new database on their server and link my app to this database. Each machine can have its own database and they can share their results with NSDistributedNotification or some other IPC/networking protocol. You can hook into the NSManagedObjectContextDidSaveNotification to track when one of the peers has committed changes locally. - Ben As you've said, is there a way the data can be always immediately written to disk so there's no 'last writer wins'? I am not using NSDocument based techniques -- it is really just one core data DB. Thank you! Regards, Tobias On Mar 17, 2010, at 10:29 PM, Ben Trumbull wrote: I am wondering whether it is possible to create a database in core data that can be opened by more than one application at the same time. It is currently impossible to handle one SQLite database with two instances of the same app. The problem is if user1 quits the app, the data is saved but user2's instance of the app doesn't recognize this file system change and just overwrites its version in memory. So the data from user1 is gone. Is there a way I can handle this? Second -- I am having more than two database versions now but still want to support my version 1.0 but the mapping model only allows one source model as well as only one target model. I would have to remove one version but that makes version 1.0 users' database unusable. Has anyone gotten something like this to work? Yes, several Apple frameworks use Core Data databases from multiple processes simultaneously with a single user account and single physical machine. Do you mean more than one application simultaneously on more than one physical computer over NFS/AFP/SMB ? Don't do that. Or do you mean an NSDocument based application using Core Data an SQLite store ? NSDocuments intentionally behave like TextEdit. Last writer wins, overwites everything. If so, you should be using a non-document based Core Data project template. - Ben - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Checking integrity of a Core Data document with SQLite store
On 14 Mar 2010, at 9:47 PM, mmalc Crawford wrote: On Mar 14, 2010, at 7:21 pm, Dave Fernandes wrote: So my question is - how do you detect this before loading the file? I'm aware of the sqlite3 PRAGMA integrity_check, but there does not seem to be a C API for this. Any pointers? http://developer.apple.com/mac/library/documentation/Cocoa/Reference/CoreDataFramework/Classes/NSPersistentStoreCoordinator_Class/NSPersistentStoreCoordinator.html#//apple_ref/doc/c_ref/NSSQLitePragmasOption See http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/CoreData/Articles/cdPersistentStores.html#//apple_ref/doc/uid/TP40002875 for an example. My understanding may be incomplete, but it seems that the example shows the use of pragmas (pragmata?) that set the state of the store at addPersistentStore...: time, and have no significant return value. PRAGMA integrity_check and PRAGMA quick_check return tables. Is there a way to collect the results of the integrity check? Would addPersistentStore...: return nil and somehow populate the error return? Yes, if it detects corruption you'll get an error back. There isn't much useful in the result tables from those pragmas. Running an integrity check can be useful if you have previously gotten a corrupt db error back from fetching or saving, or your app previously crashed, or you have some other active indicator it might be worthwhile. However, it's quiet expensive in I/O and you should not do it on every app launch / document open. Customers with account home directories on AFP, NFS or SMB servers will be very unhappy, and if your files become large enough so will people using local drives. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re:NSManagedObject awakeFromFetch not sent on secondary thread?
My custom NSManagedObject subclass uses awakeFromInsert and awakeFromFetch to setup custom object to ivar. This works as expected, but when I fetch the same object on secondary thread (in NSOperation), the ivar remains nil as awakeFromFetch is not sent... Is NSManagedObject's awakeFromFetch supposed to be sent when fetching on secondary thread? Does the secondary thread have its own private NSManagedObjectContext that you are using for the fetch, or are you fetching against an NSManagedObjectContext that was created on another thread ? If you are using an NSOperationQueue with maxConcurrency 1, then each NSOperation will need to allocate, use, and deallocate its own NSManagedObjectContext. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Problem with save as and freed managed object context
One of the other things I had been working on must have fixed the underlying problem, and my implementation of identifier and setIdentifier were actually causing this issue. I completely removed those two methods (which is of course going directly against the documentation at the top of the NSAtomicStore class reference, but going along with the implication in NSPersistentStore that you don't have to override it) and for the first time, was able to save as! Yay! But my elation was short lived because I now get an intermittent error on save as: Error Domain=NSCocoaErrorDomain Code=4 UserInfo=0x11e9c21c0 The document „Untitled60.nm5‰ could not be saved as „Untitled60sa.nm5‰. The folder doesn‚t exist. Underlying Error=(Error Domain=NSCocoaErrorDomain Code=4 UserInfo=0x11ef217f0 The folder „(null)‰ doesn‚t exist. Underlying Error=(Error Domain=NSPOSIXErrorDomain Code=2 The operation couldn‚t be completed. No such file or directory)) So sometimes it works and sometimes it doesn't. My file format is a zipped folder with my XML files and other resources in it, so when a file is saved, I create a temporary folder and put all the bits of my file into it, zip it up, and then remove the old file and move the zipped up new one into place. I'm not sure if this is relevant at all, but when I get the save error, it also says: AppKit called rmdir(/private/var/folders/nl/nlcXN-oPHJiAXXc1Z0R5VE+++TI/TemporaryItems/(A Document Being Saved By NovaMind5TP 71)), it didn't return 0, and errno was set to 66. But I am collecting the error messages in my app, and it's not anywhere in there, so must be from within CoreData, and the man page for rmdir just said that the return on error is 0 for errors, so I can't be certain what the error code means (I suspect it means that the folder is not empty). Those are NSDocument errors. It has two features that may be getting in your way (or vice versa). First, the safe save functionality. It tries to marshall all the changes into a temporary directory, and then switch the document with the old one wholesale. You're removing the old file may confuse and anger the NSDocument gods, and your switching the new one may be duplicating its functionality in a way that it's not prepared for. Pretty sure you don't want to delete the old file. The second feature is document rename tracking. NSDocument uses fs events to track any external changes to the document including renaming, deletion, or unexpected writes. By unexpected I mean anything that happens outside of one of NSDocument's write methods. So saving on a background thread directly against a MOC, or using FS commands to alter the document, will give NSDocument indigestion. Has anyone else seen that one before? I also have a problem where the save as only appears to save some of the data in my file, but I am guessing that that must be some bug in my code. If I open a file and then try to save as (without making any changes), then I get a Could not merge changes error message. I'm not sure if that's related to the problem where it is only saving part of the file, or is something completely different, but am planning to look at the data loss one first and see if it resolves the merge issue. gdb: future-break objc_exception_throw future-break +[NSError errorWithDomain:code:userInfo:] ... info threads thread apply all bt - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Checking integrity of a Core Data document with SQLite store
On Mar 15, 2010, at 7:49 PM, Dave Fernandes wrote: On 2010-03-15, at 3:30 PM, Ben Trumbull wrote: Running an integrity check can be useful if you have previously gotten a corrupt db error back from fetching or saving, or your app previously crashed, or you have some other active indicator it might be worthwhile. However, it's quiet expensive in I/O and you should not do it on every app launch / document open. Customers with account home directories on AFP, NFS or SMB servers will be very unhappy, and if your files become large enough so will people using local drives. That's a shame. It would certainly be easier to check when opening the file than to scatter code all over the app to diagnose the problem. Is there a standard exception I can expect to get when fetching or saving? NSInternalInconsistencyException is the one I currently get. You should generally get an NSError with the code that is NSFileReadCorruptFileError. In gdb, what does: future-break objc_exception_throw future-break +[NSError errorWithDomain:code:userInfo:] ... info threads thread apply all bt say ? The only place you should get an exception is if you try to fault in an object that is unreachable. Either because the database was deleted or corrupted, or because another thread / context deleted that object you wanted before you and you only held the reference instead of getting all the data at once (e.g. race on delete) - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Core Data Autoincrement?
Is there a way (in a Core Data entity) to make an attribute that's like a relational DB autoincrement field? Or, failing that, does an object of class (or subclass) NSManagedObject inherit a unique UInt32 value that can reliably differentiate it from any other instance being managed by the NSManagedObjectContext in use? Each managed object has an objectID that is unique and automatically assigned. NSManagedObjectID are opaque value classes, but you can serialize the -URIRepresentation of an objectID. Mutating the URI form and then reimporting it is unsupported with extreme prejudice. If you must have an int32 autoincremented, you'll need to create your own. You can create an entity with a single row to store the integer and update it transactionally. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Problem with save as and freed managed object context
Further information: I have just gone through every place in our code where it makes reference to the managed object context (there's only 90 of them thank goodness), and there is nothing there where we either retain or release a managed object context, or use one in a notification or anything like that. I have also found out that the managed object context that is over-released is not the one that I have in my persistent document, so all I can assume is that it is one that is created behind the scenes programmatically during the persistent store migration. I also checked our will/didTurnIntoFault and awakeFromInsert/Fetch to make sure they were clean. Found a few minor problems, but it made no difference. I did find that awakeFromFetch was called during the process of migration, but didn't find out anything else of interest. I also checked for description methods that may have caused faults to fire at the wrong time. I only found one description on one of our managed object classes, and an interesting thing happened when I commented that out - I could not save at all (trying to send an objectID message to an NSNumber). I have not tracked that one down yet. Try running with MallocStackLoggingNoCompact=1 set in the env and using malloc_history to see where an NSManagedObject was allocated and then freed and replaced with an NSNumber. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Core Data Spotlight: Record-Level Indexing : Issues?
On Mac OS X v10.6 and later, for non-document-based programs, you can create Spotlight indexes where each record is indexed individually. [1] I interpret this to mean that users can get results for my app's records in their Spotlight searches, the way they get Safari bookmarks and Address Book contacts. Before I get in too deep, I was hoping someone could advise on potential trouble areas: 1. My app is document-based, although in the use-case for which this feature, the user has only one document. I'd like it to index that one. I presume that by non-document-based programs, Apple is actually referring to the store -- in other words, if I provide in my document-based app a non-document-based store, Spotlight can index the records in that store. So I'll copy objects from the user's selected document into the new non-document-based store. Should it work? Does anyone have a more elegant suggestion? I'm not sure what user experience you are trying to effect here. It's sounds like you'd be better off if this part of your app was just not document based at all. The HI guidelines are such that document based apps should not perform row level Spotlight indexing. You can see that is the case empirically with the Spotlight menu. 2. Although you can use your custom managed object classes in the importer, the Core Data External Record daemon ... canâ•˙t use custom managed object classes. All of my managed objects are *subclasses* of NSManagedObject. Is that what they mean by custom managed object classes? It means within the importing code you'll just have NSManagedObjects encapsulating your data. All your persistent properties defined in the model will be accessible, but any custom methods or business logic will not. If so, then the Core Data External Record daemon won't be working for me, but it's optional and only to improve performance. On the other hand, my objects have several dozen properties, only a few of which will be indexed. So if I need to implement another non-document store anyhow, maybe I should copy my objects to simple NSManagedObjects, copying only the few properties which I want indexed by Spotlight or are necessary to act upon a hit. 3. Project currently uses Mac OS 10.5 SDK. If I still want to support 10.5, I need to bite the bullet, read the Cross-Development Programming Guide and configure the project for cross-development, but in the end, Record-Level Indexing should work for my 10.6 users. 10.5 users simply won't get any of my results in their Spotlight searches.Any known gotchas in there? In theory, but since your app is document based, the preferred approach would be to use the whole document spotlight importer. 4. Can't find any sample code. It would be nice to see that Record-Level Indexing has been done? You can create a new project with the template and it should be a functional sample. Both document based and non-document based Core Data projects have templates with spotlight importers. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Trouble with core data and Save As
I'm having another look at an issue I posted about a couple of weeks ago, where Save As was causing an error. At the time, I was using a custom managed object context. I have now reverted to a standard managed object context. I do not create or release this managed object context anywhere - it gets automatically created when I first access it from my persistent document...but this context is not where the problem occurs. When I do a Save As, it migrates to a new store behind the scenes, it appears to load my objects into the new store OK, and accesses the attributes OK, but then I get a crash where it tries to send a message to a deallocated NSManagedObjectContext. This context is created by core data internally as Instruments shows: # CategoryEvent Type RefCt Timestamp Address Size Responsible Library Responsible Caller 0 NSManagedObjectContext Malloc 1 00:28.911 0x11d0d58a0 240 CoreData-[NSPersistentStoreCoordinator(_NSInternalMethods) _retainedAllMigratedObjectsInStore:toStore:] 1 NSManagedObjectContext Autorelease 00:28.953 0x11d0d58a0 0 CoreData -[NSPersistentStoreCoordinator(_NSInternalMethods) _retainedAllMigratedObjectsInStore:toStore:] 2 NSManagedObjectContext Zombie -1 00:28.955 0x11d0d58a0 0 CoreData-[NSManagedObjectContext(_NSInternalAdditions) _dispose:] As far as I can see, I never get a look in. It all appears to be triggered by [NSPersistentStoreCoordinator migratePersistentStore:toURL:options:withType:error:]. Instruments can get confused by custom -retain / -release methods, so those may not be all the events. But obviously there must be something somewhere in my code causing this problem. I am just totally clueless as to where to look, and would appreciate any suggestions. I thought there might be something happening in one of my awake... methods or a dealloc or willTurnIntoFault or didTurnIntoFault, but I didn't find anything that looked suspicious. The store is an NSAtomicStore subclass, and I read that there could be problems with Save As if newReferenceObjectForManagedObject: did not return a constant value, but that isn't called before it crashes...so I'm stuck. Any ideas? The custom store seems like the most probable source of issues. One source of problems I've seen is when a custom store doesn't handle either the metadata or the objectIDs correctly. The store UUID and the object's IDs need to be stable (e.g. if we ask your store for its UUID, it doesn't make up a new answer for every question). If you give us an objectID to assign to an object, you cannot later change your mind. And then there's just not preserving the contents of the metadata. If you can create a stripped down sample project that reproduces the issue, you could file a bug. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: NSNull vs nil in NSManagedObject setValue: v forKey: k
NSManagedObject* obj; // gets created somehow [obj setValue: nil forKey: @bar]; // succeeds where NSDictionary fails [obj setValue: [NSNull null] forKey: @bar]; // fails where NSDictionary succeeds so - this is conceptually buggy thinking and the thoughtful developer could be forgiven for being VERY SURPRISED by this behavior that is NOT DOCUMENTED. A review of http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSNull_Class/Reference/Reference.html clearly documents the behavior. Additional inferences beyond the documentation are unsupported. The NSNull class defines a singleton object used to represent null values in collection objects (which don’t allow nil values) None of the examples involve KVC. There are no references to KVC in the related documentation. NSNull isn't a Key Value Coding thing, this is an NSArray, NSSet, NSDictionary thing. Nothing in the documentation implies otherwise. Secondly, NSDictionary is untyped. NSNull is valid for any key. NSManagedObject properties have an explicit type. Values are expected to conform to the type specified for the property named by the key. NSNull is not an NSString. Your attempts otherwise are a basic violation of OOP. Thirdly, nil and NSNull are both valid values for typed NSManagedObject properties but they are not for NSDictionary. In an NSDictionary, NSNull and nil must be equivalent because there are no other legal possibilities. nil is never a valid value. But for either transformable or transient NSManagedObject properties, both NSNull and nil are legal values. Should we make it illegal to have optional properties that use NSNull ? In general, NSManagedObject does not perform value type coercion. We chose this to balance several design criteria including detection of developer error (e.g. masking bad values), performance, and convenience (doing this yourself is trivial). If enough developers feel another balancing would be more productive, we'd be happy to reevaluate that. You should contribute by filing bugs. - Ben p.s. On an ancillary note, gratuitous use of NSNull outside of collections gets tedious problematic. Since NSNull != nil, all the client code then has to check multiple conditions, which this is easily forgotten, and then selector not recognized bugs crash apps because NSNull isn't the right class. ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Core Data 10.5 Mapping error 134140. Troubleshooting, Value Expressions?
In my latest model revision, I added a relationship between existing entities. The value of nil would be fine, so I typed 'nil' in for Value Expression on each end. The compiler swallowed it OK. Also, I added a Date attribute, which I would like to default to NSDistantFuture, but since I didn't know how to enter that, I entered 'nil' there also. Should these entries work? You don't need to set optional property values to nil. For relationships, you probably want to use the default expression. Is there any documentation which defines the syntax to be used for Value Expression? The two paragraphs at the bottom of this page [1] show a simple example but not much detail. The value expressions are the same as NSPredicate expressions. Also, NSMigrationMissingMappingModelError = 134140 - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Core Data design question: receiving KVO notifications of partially mutated objects
What is considered best practice when it comes to mutating many properties of a managed object, specifically with regard to KVO observers getting notified before all mutations are finished? This is a problem intrinsic to the design of KVO. KVO is all about fine grained per property notifications. It's very well suited to that. However, it's common for complex objects to be in an intermediate state during a KVO notification. There's no better notion of cross-property coherence than refactoring into a different property object that encapsulates everything. Let's say I have an Rectangle object. It has properties: colour, width, height. Imagine some controller observing all these properties, perhaps to trigger a redraw. If I do: [rect setColour:...]; [rect setWidth:...]; [rect setHeight:...]; In this example, the best KVO can do is work with a colored rectangle object that in turn is the 1 property you work with here. So, this would be an example of a granularity of notification that works better with NSNotificationCenter This is short and readable. But observers will be notified after each setter. yup This could be a problem if intermediate states are not self- consistent and could also lead to unnecessary redrawing. And many other performance issues if observers recalculate things too aggressively. In the general case, I might not even know who is observing. Well, that's the point of the notification pattern. I guess it's safer to create a setColour:width:height: method in my NSManagedObject subclass that does: [self willAccessValueForKey:@colour]; [self willAccessValueForKey:@width]; [self setPrimitiveColour:...]; [self setPrimitiveWidth:...]; [self didAccessValueForKey:@width]; [self didAccessValueForKey:@colour]; Is this what I should be doing? Generally, no. (setting aside, the calls should be to willChangeValueForKey:, btw) If your issue is that drawing or recalculation is occurring too frequently after KVO changes, you can consider coalescing and deferring the observers' actions instead of performing them synchronously. This can be valuable even for less complex KVO issues. You could also refactor the 3 properties into 1 object. Or use an NSNotification instead of KVO to adjust the granularity of notifications to better suit your drawing code. This doesn't really have anything to do with Core Data. However, for NSManagedObject, Core Data already provides the change coalescing and NSNotifications for you. You can respond within NSManagedObjectContextObjectsDidChangeNotification instead. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Unable to open Core Data documents
I recently recompiled an existing project for 10.6. I made no changes to the code. However, the resulting binary is no longer able to open older files created by this program. The error message is: The document „Budget.drachma‰ could not be opened. drachma cannot open files of this type. Hard to say with the information you've provided. You should try in gdb future-break +[NSError errorWithDomain:code:userInfo:] and getting both a stack trace that's generating the error as well as the actual NSError object and its userInfo dictionary. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Could not merge data-error on save in a single moc app
On 29.10.2009, at 20:05, Ben Trumbull wrote: I get a Could not merge changes-error on save in a single moc app (*). The docs state this is a problem of a multi-moc setup: http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/CoreData/Articles/cdTroubleshooting.html Yes, the MOC you are saving refers to data that has changed out from underneath it in either the PSC or the database file itself. This is an optimistic concurrency control failure. The error does not appear on every system and seems to be related to when save is called. (race condition?) So far I only have users complaining that run Mac OS 10.6. I can rarely reproduce the error myself. Any pointers where to look? Well, you can break on -[NSManagedObjectContext init], and -save: to see if you really only have 1 MOC. Other than that, breaking on + [NSError errorWithDomain:code:userInfo:] and doing thread apply all bt in gdb is usually helpful. I use garbage collection. regards Ruotger (*) the context is handed out by a singleton method. I NSAssert() this method is only called by the main thread. Well, it's still possible for code to leak it to another thread if, you say, pass NSManagedObjects around, and then ask them for their MOC. - Ben Hi, I've checked all accesses to the moc and by using mogenerator I generated files that assert that every single access of a managed object is on the main thread. I still get the Could not merge data- error or sometimes this one: Which is neither here nor there. This is a version conflict error not a multi-threading error (though MT errors can lead to versioning errors) Printing description of error: Error Domain=NSCocoaErrorDomain Code=1550 UserInfo=0x5d45490 balances is not valid. balances is a one-way to multiple relation. Any more ideas? Same as last time, break on the error, get it's userInfo, and a stack trace of all the threads in the app. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Help debugging Dangling reference to an invalid object. Core Data error
On 10/29/09 5:23 PM, Ben Trumbull said: Even if I do [scene addTargetsWeak:[scene targetsWeak]] I get the error. Does this make any sense to anyone? The only caveat is you cannot change relationships from within - awakeFromFetch. This is documented: Ben, This is something I know and avoid, but it turns out this is what's happening afterall! Thanks! Is there some way to catch such violations? I suppose I could implement all relationship mutating methods, call backtrace(), and look for awakeFromFetch. :) Not really. But you can file a bug, and we can add an assertion to the debug library easily enough. Or we can enable change tracking in -awakeFromFetch. But that would mean generic setters will dirty newly fetched objects, and their containing documents, which people objected to, and resulted in the current behavior. Basically, I have a controller class that uses KVO to observe all kinds of things. And I'm suffering from a kind of spaghetti chain of KVO and faulting. In my observeValueForKeyPath:ofObject:change:context: I sometimes end up firing a fault of some object, which causes me to reenter my observeValueForKeyPath: for some other 'context', which ends up firing a fault of some other object, etc. At some point awakeFromFetch is in the backtrace and at some later point I mutate a relationship. Ick. Well, you can use prefetching to disentangle dependent controllers from firing too much stuff too lazily, or custom controller classes that override - (BOOL)fetchWithRequest:(NSFetchRequest *)fetchRequest merge:(BOOL)merge error:(NSError **)error; - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Core Data design question: receiving KVO notifications of partially mutated objects
If your issue is that drawing or recalculation is occurring too frequently after KVO changes, you can consider coalescing and deferring the observers' actions instead of performing them synchronously. This can be valuable even for less complex KVO issues. You could also refactor the 3 properties into 1 object. Alas, that would require 1) persistent store migration (painful on 10.5) and 2) I find it very handy to be able to bind 'colour' to a colourwell and 'width' and 'height' to text fields. Perhaps I have bent my model too much in favour of my UI also. :) But anyway, this 'Rectangle' object was just an example. My real app is a bit harder to explain. Or use an NSNotification instead of KVO to adjust the granularity of notifications to better suit your drawing code. This doesn't really have anything to do with Core Data. However, for NSManagedObject, Core Data already provides the change coalescing and NSNotifications for you. You can respond within NSManagedObjectContextObjectsDidChangeNotification instead. Perhaps NSManagedObjectContextObjectsDidChangeNotification is the thing I have overlooked! I currently use it almost nowhere but have tonnes and tonnes of KVO observing going on. I'll have to RTFM on NSManagedObjectC ontextObjectsDidChangeNotification. Questions that pop to mind though: is it safe for me to fetch? to create/delete/mutate ManagedObjects? I'd recommend performing a selector after delay 0. You can inspect the objects and decide what you want to do with them. The notification is posted from within the change tracking code, so making changes could possibly result in them getting caught in between 1 user event and the next. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: EXC_BAD_ACCESS when migrating a store, but only sometimes...
I've come across a rather perplexing problem which is driving me nuts. I'm attempting to migrate a core data SQLite store to the current model version, and most of the time it works fine. However, sometimes I get an EXC_BAD_ACCESS in the following stack: objc_assign_strongCast + 19 -[NSMigrationManager migrateStoreFromURL:type:options:withMappingModel:toDestinationURL:destinationType:destinationOptions:error :] + 2750 (My methods...) If you can reproduce it in a small sample project (with your models, etc, but the least amount of custom code), then please file a bug. One thing you might try is, before creating the NSMigrationManager with -[NSMigrationManager initWithSourceModel:destinationModel:] is creating 2 empty throw away PSCs, one that uses the source model and one that uses the destination model. Then just release the PSCs and use those models to construct the NSMigrationManager. That should work around the one known GC issue here. If you're not using GC, than try breaking out Instruments' ObjectAlloc with NSZombie detection. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Could not merge data-error on save in a single moc app
I get a Could not merge changes-error on save in a single moc app (*). The docs state this is a problem of a multi-moc setup: http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/CoreData/Articles/cdTroubleshooting.html Yes, the MOC you are saving refers to data that has changed out from underneath it in either the PSC or the database file itself. This is an optimistic concurrency control failure. The error does not appear on every system and seems to be related to when save is called. (race condition?) So far I only have users complaining that run Mac OS 10.6. I can rarely reproduce the error myself. Any pointers where to look? Well, you can break on -[NSManagedObjectContext init], and -save: to see if you really only have 1 MOC. Other than that, breaking on +[NSError errorWithDomain:code:userInfo:] and doing thread apply all bt in gdb is usually helpful. I use garbage collection. regards Ruotger (*) the context is handed out by a singleton method. I NSAssert() this method is only called by the main thread. Well, it's still possible for code to leak it to another thread if, you say, pass NSManagedObjects around, and then ask them for their MOC. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Help debugging Dangling reference to an invalid object. Core Data error
On Oct 29, 2009, at 16:55 , Sean McBride wrote: On 10/22/09 4:59 PM, Melissa J. Turner said: I have an entity 'Scene' with a to-many relationship to an entity 'Target'. The inverse relationship is also to-many. Both relationships are optional and the delete rule for both sides is nullify. To repro, I delete all Scenes then try to save. It gives: Dangling reference to an invalid object. = null; NSAffectedObjectsErrorKey = ( BSScene: 0x2004ff940 (entity: Scene; id: 0x2004f32a0 x-coredata:// FCE3E0E3-F187-4C44-BFC3-60D7AF3E579F/Scene/p343 ; ... This error gives only 4 hits with Google. :( The problem is that some Targets still have relationships to some Scenes! How can that happen? It seems like the delete rule is not doing its job. Does the object with the ID x-coredata://FCE3E0E3-F187-4C44- BFC3-60D7AF3E579F/Scene/p343 exist in the affected store? What happens when you call existingObjectWithID:error:? Do you know what version of the OS the bad documents were created on? A lot changed in the relationship handling area in 10.6 (for the better, really and truly ;-), so knowing whether your customers created them on 10.5.x or 10.6.x would help narrow down the possibilities. Thanks Melissa and Ben for your replies! I have been working on this issue for days now and am getting increasingly confused. :) My coworker found a way to create a document (in 10.6.1) with almost the same problem as originally described (it complains about the inverse relationship instead). Examination of the XML document (as text) suggests that the object graph is ok. To repro: 1) open said document 2) delete a particular managed object 3) attempt to save - receive dangling reference error. Repros 100%. I've also found that changing a particular line of my code prevents (100%) the error from occurring. Said line only runs when the document is opened (as a result of a controller getting a KVO notification). The entities in question are Scene and Target. Scene has a to-many 'targetsWeak' relationship. Target has a to-many 'scenesWeak' relationship. They are inverses. They are optional and nullify. The code snippet in question: NSSet* currentTargets = [scene targetsWeak]; NSSet* additionallyDesiredTargets = some fetch result; BOOL isEqual = [currentTargets isEqualToSet: additionallyDesiredTargets]; assert (isEqual); //if (!equal) { [scene addTargetsWeak:additionallyDesiredTargets]; } It turns out that isEqual is always YES in this limited repro case. Therefore, I don't really need to change 'targetsWeak' (the 'if' body). But if I do anyway (the commented 'if'), I get the 'dangling reference' error. If I don't, I don't. Even if I do [scene addTargetsWeak:[scene targetsWeak]] I get the error. Does this make any sense to anyone? The only caveat is you cannot change relationships from within -awakeFromFetch. This is documented: http://developer.apple.com/mac/library/documentation/Cocoa/Reference/CoreDataFramework/Classes/NSManagedObject_Class/Reference/NSManagedObject.html#//apple_ref/occ/instm/NSManagedObject/awakeFromFetch Otherwise, if this repros 100% of the time, file a bug and we'll take a look at it. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Core Data: Undoing Cut-Paste, Drag-Drop between documents
Although some users might expect that the employee moves from Document 2 back to Document 1, that does not happen. Because each document has its own managed object context and own undo manager, because Document 2 is active, the employee disappears from there. But order to make it reappear in Document 1, user also needs to activate Document 1 and click Edit Undo *again*. One could argue that there is no problem. Mac Users understand that Undo is per-document. They will instantly realize that they did two *do* operations in two different documents (Cut in Document 1, Paste in Document 2) to get where they are at, so they expect that two *undo* operations, one in each document, will be required to get back. A highly-skilled Core Data developer might even be able to set the correct undo actions, Undo Paste Employee and Undo Cut Employee, which will help cue in those users with unrealistic expectations. But this answer is not as plausible if the user makes the move using drag-drop instead of cut-paste. (Drag-drop is not implemented in DepartmentAndEmployees, but it is common in real apps.) Clearly, the user has now performed only one operation, and might rightfully expect it to be undoable with a single Undo. I suppose that the developer of such an app could replace the undo manager in each document, upon creation or loading, with a single, common undo manager. But I fear that this would cause hundreds of unintended under-the-hood consequences in Core Data, and am loathe to think about it. Has anyone ever done this? Another alternative: In the drag-drop implementation, before doing the *delete* operation, temporarily disable undo registration in the source document. Then, explicitly register with the undo manager of the destination document an action which would insert an object with the old object's attributes into the source document's moc. An equivalent trick would probably work in a non-Core-Data document, but I suspect that Core Data's under-hood magic wouldn't be too pleased with this. In either case, there's the little issue of what if the source document is closed, then later this Undo action gets invoked. Setting a flag to say that you've got Document 1's stuff on your undo stack, and removing all undo actions when Document 1 is closed would probably be a good idea, but this is starting to get messy. You can queue up and control the undo actions in each document's undo manager. But you're still left with the problem that undo will be wired to the front most document, and there's no meta-undo manager for cross-document undo operations. I don't see how this is a Core Data issue. Either you can coordinator undos across multiple different undo managers, or you can't. Well, I suppose unless you want to have all your documents share an undo manager, which Core Data won't like. But that's crazy talk :-) As a user, I would never expect undo to go modifying the contents of other windows in a document based app. Take TextEdit for example. The behavior you suggest is completely inappropriate for TextEdit. I have separate documents because I'm working on separate things. I'd be pretty frustrated if I couldn't move things between documents without borking undo. The behavior you describe is more commonly associated with non-document apps like iCal or Address Book. They use a single undo manager for the user edits. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Search on Core data Table issue?
Not sure if this is the right place (I am sure someone will let me know if it is not) I have a iPhone application that has a UITable view that is backed by core data. I want to perform a reducing search so that only the items starting with the characters entered into the search bar are shown. This is normally done with delegate - (void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *) searchText no problem. I am a little confused as I am new to Core Data how to do this. One of the big problems as I see it is going to be updating the interface to let it know what to present. I assume an alternative NSFetchedResultsController needs to be sent to the UITableView. You're basically on the right track. So here are my issues: 1) I assume I need to create a NSFetchedResultsController with only the correct items in it then tell the UITableView to use this as the dataSource and reload the table? Yes. Most of the Apple apps use an overlay, so it's also a different UITableView entirely. See search in the Contacts app, for example. 2) is there a better way than executing a full sorted fetch and removing those objects that do not conform. ie is there a way of doing a select where type fetch? I'm not sure exactly what you mean by select where type fetch. What type ? The typical pattern is if the user typing has gone from a 0 length string to a 1 character string, you'll need to do a fetch. (that includes the user deleting a character and starting over). If the string already has 1 or more characters, you can choose between a new fetch, or simply an in memory filter of the previous results. The results should always be sorted, so you can binary search the range to filter, instead of filtering all the strings. The balance of when to fetch and when to filter is complicated, and depends on your data set, as well as your UI. If you have an extremely large data set, you should use a fetch limit, so that the user cannot get back all the results until they've entered a sufficiently discriminating search string. If the number of results you get back is equal to the fetch limit, you can consider executing a count request or just saying and more If the number of results is small, then it's much faster to filter the old results in memory. Fetching will save you a lot of memory, but it is I/O so it has a high fixed cost (like ~2ms on a desktop). You'll want to balance the two. You might start out with a threshold of 100 and tune from there. Thanks in advance and sorry if this is a dumb question. It's not. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Help debugging Dangling reference to an invalid object. Core Data error
Core Data is giving me a validation error when I try to save a document after making a simple change. I have an entity 'Scene' with a to-many relationship to an entity 'Target'. The inverse relationship is also to-many. Both relationships are optional and the delete rule for both sides is nullify. To repro, I delete all Scenes then try to save. It gives: Dangling reference to an invalid object. = null; NSAffectedObjectsErrorKey = ( BSScene: 0x2004ff940 (entity: Scene; id: 0x2004f32a0 x-coredata:// FCE3E0E3-F187-4C44-BFC3-60D7AF3E579F/Scene/p343 ; ... This error gives only 4 hits with Google. :( The problem is that some Targets still have relationships to some Scenes! How can that happen? It seems like the delete rule is not doing its job. This error happens when the MOC for a destination object is nil, the object is in a relationship but marked deleted, or the object in a relationship has a temporary objectID but is not marked inserted. Using refreshObject:mergeChanges:NO on an object with pending changes can do some of that, as can assigning a deleted object to new relationships after delete propagation has run. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: odd behavior with NSError?
(response is pedantic for the purposes of the archive :) even more better flaming pedanticism! On Oct 15, 2009, at 10:41 PM, Nathan Vander Wilt wrote: Ouch. So the following pattern is incorrect? Yes; it is incorrect. NSError* internalError = nil; (void)[foo somethingReturningBool:bar error:internalError]; if (internalError) { // ... } Specifically, assuming anything about the value of 'internalError' without first determining the return value of - somethingReturningBool:error: returned a value indicating an error (typically NO/0/nil/NULL) is an error. The specific issue is failing to check the return value of the method before touching the internalError value. You MUST check it. However, the documentation also encourages not assigning nil to local NSError* declarations. Not initializing locals is imho, professionally, realistically, and morally wrong. It's just a bug waiting to happen. And you have to know it is, just looking at it. Whatever might have been saved / gained by not initializing them was wasted, for all of time, the first time I had to debug the segfault. Which I did, like an idiot, a long time ago. Let me just say I really especially appreciated the time spent debugging other people not initializing their locals. It wasn't visions of sugar plums. Other people have different perspectives on local variable initialization. Wrong perspectives, but different. Fortunately now, they waste their arguments upon the merciless http://llvm.org/img/DragonFull.png - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Default Core Data errors
Do you check, and at least assert, if any API that has an NSError** parameter returns one ? (typically a return value of NO or nil). For Core Data, you'll always want to check adding a store to the coordinator, saving, and fetching. For your documented based app, the NSDocument APIs can also return errors. When you get an NSError, you'll want to also log its userInfo dictionary. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: CoreData could not fulfill a fault after Save As
The short description is this - I have a document based CoreData app. I can carefully craft a set of data. I then open the document, select a particular record, and do a Save As. This works fine. But when I select a second record, I get errors that CoreData could not fulfill a fault. If I quit the app and re-launch it, I can operate on the copy I just saved w/o issue, so apparently the data is there. I haven't a clue what the problem is. Does your app run correctly on 10.5.* ? There's a known regression similar to this in 10.6.0 and 10.6.1. If this reproduces on a system other than 10.6.0 or 10.6.1, please file a bug. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Default Core Data errors
On Oct 14, 2009, at 12:21 PM, Rick Mann wrote: In the simplest case, I don't create any entities. I don't override any of NSPersistentDocument's persistence-related methods. I just save the new untitled document, then try to re-open it. You can override the methods declared in NSPersistentDocument.h particularly configurePersistentStoreCoordinatorForURL and readFromURL, and just call super. But look at the BOOL result and if NO, the NSError. You can also grab the error in the debugger as others have suggested. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: More Core Data Questions
On Oct 13, 2009, at 3:17 AM, Jon Hull wrote: You don't need proxies or NSProxy for this. You can just use a delegate. Off the cuff, I might consider implementing a protocol on your immutable objects for methods like currentLocation and currentPlayer which vector through a semi-global context object not unlike the app delegate. Then you can use keypaths to naturally write currentPlayer.name and so forth. NSProxy is unnecessarily heavy weight for simple delegation. They use memory too, ja know. And the semi-global context allows you to avoid making all your objects larger. You might even model the current state as a formal entity. That buys you change tracking, undo, multi-writer conflict resolutions and the ability to easily persistent and search for it. It also allows other managed objects to work with it naturally. Hmm... something to consider. The proxies do allow some powerful runtime state effects. The currentLocation was just a simple (and often used) example, but it is easy to have proxies which represent The sister of the person I am currently talking to or the location of that person or the item which that person holds in their hand. They can also be used to represent groups of characters. This is very powerful, and has come in very useful. Still, it might be possible to get the same functionality in another way. Okay. Typically the Cocoa way of doing that is by name and using keypaths instead of by pointer value and NSProxy. and then call off to the manager with the id whenever they need to run an event. Inside the manager I would either call off to a small sql database with blobs holding freeze-dried (keyedArchiving) objects an id column, not a great plan. No searching into blobs, no partially loading or saving the larger freeze dried objects. hmm... The largest blob would probably be an object with an array of 20 or so pointers/ids. Not sure I need to search into them... mostly I just need to grab them by id. I had considered just using core data for everything, but as I mentioned in a previous post, I *need* to have consistently ordered arrays of these immutable objects (which can be in multiple places in a single array, and can be in multiple arrays). This is apparently difficult using core data :-( It's not difficult, although it is a bit tedious. Ordered relationships require you model the join table between two entities yourself (for many-to-manys), and add an ordering attribute. For one- to-many, you can put the ordering attribute on the destination entity (no join table necessary). Although, now that I think about it, perhaps I can store *just* the array of ids as a binary property, and have everything else defined in the entity. I will have to do some experiments. Yes, you could do that. Also, you could do that trivially with Core Data and just be done. Do you mean store the blobs in a core data managedObject instead of a SQL database? yes or avoid the blobs entirely using core data? Probably Unfortunately, it sounds like you don't have a ready alternative besides to spend a considerable amount of your own engineering resources. You'll have to decide if learning Core Data, and tuning your app performance fits within your schedule requirements, and whether implementing, debugging, and tuning all this yourself will really take any less time. You might consider mocking up a Core Data version over a few days and seeing how far you get. Yes, I think I will try that. Any advice on how to handle the 2 different types of graphs mentioned in my earlier post? Ideally I should have 2 files. One holding the event tree and one holding the rest (i.e. the stuff that changes). They main problem there is that they have connections between each other (e.g. an event might take a location or character as a parameter) Just create two stores and add them to the same NSPersistentStoreCoordinator. The connections between them can be represented via keypaths (bound dynamically) or persistently by stashing the objectID's URI away and materializing it in a transient relationship in -awakeFromFetch - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Saving for iPhone - Can core data handle this?
Jon, Your question is a bit amorphous. Can Core Data do something like this ? Sure. May it require adjusting things to fit into its architecture ? Possibly. I have a game project for the iPhone which has a rather complicated object graph Well, it would probably only take a few minutes to mock something up in the model editor in Xcode. That would help make your question more concrete. There is a large graph of tiny immutable objects which represent the story (including different branches of the storyline). How tiny is tiny ? People have widely different ideas about tiny and huge and the difficulties of various problems. This graph is large enough that I only want to keep the nodes that are actually being used in memory. 100 objects ? 1,000 ? 100,000,000 ? The largest db in a deployed iPhone app using Core Data that I know of is about 450,000 rows. (FDA warning: results not typical) There is also a separate graph of game objects (characters, etc...) that change state over time and will need to be saved as part of a saved game. These seem like they should be stored separately since one never changes and will be common for all players, while the other is different for each player. I also want to be able to get a fresh copy of the changing objects for new games. Is this for local or remote data ? The final complication is that I have several proxies being used in an important way, and I don't think there is any NSProxy equivalent for NSManagedObject. I guess I would just have to hope that NSManagedObject doesn't have any methods that the targets of the proxy override. Uhm... Why ? I don't think NSManagedObject will be happy with NSProxy and vice versa. I'd recommend against this. If you need to use NSProxy for something else, then I'd recommend adding an observer to the NSManagedObjectContext and proxying your custom observer. Or, you can use composition to create your own object that has an NSManagedObject || NSManagedObjectID ivar and then proxy that. I have been reading the docs for Core Data, and have a rough idea on how I might accomplish all of this, but I wanted to see if someone had experience with this and could keep me from spending time going down the wrong path if it isn't going to work. So, do you think core data can handle this? What approach (roughly) should I use? - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: CoreData Bug? (SQLite vs XML) + isolated reproducible case
Thanks for the test project. However, reviewing and fixing all compiler warnings is likely to make development a significantly less frustrating experience. We've taken to fixing (nearly) all compiler warnings, even ones we know are harmless, so we can easily find the new ones that likely aren't. A lot of people like -Werror for exactly this reason, but that's a bit too draconian for my taste. CoreDataBug_DataModel.xcdatamodel:SmartFolder.sources: warning: SmartFolder.sources -- to-many relationship does not have an inverse: this is an advanced setting (no object can be in multiple destinations for a specific relationship) - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: More Core Data Questions
Ok, let me ask some more specific questions and see if that gets a response... Feel free to respond if you only know the answer to 1 or 2 of these. 1) Can I count on a to-many relationship keeping the order of the managedObjects it points to? The order is very important in this case, and I need a way to ensure that the order does not change when the object is saved and reloaded. No. You'll have to model order as an attribute yourself. 2) Does core data require a run-loop to work? No. 3) What is the best way of connecting objects from different stores? I am considering giving them UUIDs and then storing that as a reference. Then setting a transient property based on that in - awakeFromFetch. Alternatively, I could store it as a fetched property, but I want it to be a 1:1 correspondence. That's fine, although you can just use the URI representation of the destination object instead of creating your own separate UUID. You might look at /Developer/Examples/CoreData/iClass 4) Is there a better way to get this lazy loading? What was the first way ? My main goal is to keep only those objects from this large (1000) object graph in memory that are needed (since the iPhone has limited memory). You may find the NSFetchedResultsController useful, as well as the options on NSFetchRequest like -setFetchBatchSize: They are very aggressive about memory optimizations. Basically, I want the behavior of the old resource manager from the olden days (that is I can act as if my full object graph is in memory, but only those that are needed actually are... and they are fetched just in time). Core Data always does that. That's the default with its SQLite persistent store. Are you making this more complicated than it needs to be for performance issues you have yet to measure ? I wouldn't add multiple stores to work around a performance issue before actually trying it. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: More Core Data Questions
The project is a game engine which has 2 graphs. The first is a tree of events that represent the story. Each event in the story is an immutable object, and there is a special event which represents a series of events to run in order and one which represents branches that the player has to choose from. All of these are immutable, and the main goal is to avoid loading the whole graph which will consist of 10k-50k events. This graph will be the same for every player. Okay. If you build this immutable store on your dev machine and include it in your project resources, you should see excellent performance. Creating a store this size on the device would take a while. Just don't go crazy with to-many relationships or database normalization (for this many objects, on a phone). Of course, you don't want to search across all the events all the time. You'll want some criteria (NSPredicate) to evaluate just the subset of relevant elements. I also use proxies as context switches to objects in this graph. A concrete example is the current location, which is a proxy that pretends to be a location, and forwards all the messages it receives to the location object where the player is currently located. This allows events which reference locations/players/etc that change depending on state to remain immutable. You don't need proxies or NSProxy for this. You can just use a delegate. Off the cuff, I might consider implementing a protocol on your immutable objects for methods like currentLocation and currentPlayer which vector through a semi-global context object not unlike the app delegate. Then you can use keypaths to naturally write currentPlayer.name and so forth. NSProxy is unnecessarily heavy weight for simple delegation. They use memory too, ja know. And the semi-global context allows you to avoid making all your objects larger. You might even model the current state as a formal entity. That buys you change tracking, undo, multi-writer conflict resolutions and the ability to easily persistent and search for it. It also allows other managed objects to work with it naturally. The event graph is traversed from a background thread which basically runs through events in order until it needs a user response. Surely most of the events are not relevant most of the time, no ? By location, if nothing else like pre-requisiste events. If you can keep a cookie into the player's choices tree, then you can do an indexed query on eligible events and only evaluate those in memory. I have a controller set up which communicates with the main thread, and the background thread blocks until the controller gets a valid response, at which point it starts unwinding again until it needs another response or reaches the end. There is no run loop for this thread. sure. don't forget an autorelease pool. All of this works fantastically for the game, but now I have reached the point where I can no longer put off save load. When I started the project Core Data was not yet available on the iPhone, so the design did not take it into account. I was thinking that core data might be able to help me save load these graphs, but now I am not so sure. My new plan is as follows: Create a manager singleton for events which returns an event object for a given id, and then modify the special events mentioned above to hold an event's id instead of actual event objects, You could do that. Probably easier to just use an NSManagedObjectContext and the delegate I mentioned above. and then call off to the manager with the id whenever they need to run an event. Inside the manager I would either call off to a small sql database with blobs holding freeze-dried (keyedArchiving) objects an id column, not a great plan. No searching into blobs, no partially loading or saving the larger freeze dried objects. Also, you could do that trivially with Core Data and just be done. or save each event as a file with the id in the filename. Each event file would have to be 8K before this even begins its distant journey to approach making any sense at all from a performance perspective. I would also have a cache in the manager to avoid reallocating objects unnecessarily. I wouldn't worry about caching just yet. Bigger fish. I would prefer to have something like core data do this lazy loading for me, but it may not work in this case. For the objects that store state, I will just archive them into a save file. still no lazy loading or incremental saving. Thoughts? Does this sound like a reasonable approach, Not really, no. Maybe you need to do something quick very dirty for deadlines. c'est la vi or will core data actually work for me? It's not clear to me why you think it wouldn't.So far, all I get is that you haven't used Core Data before and don't know it. Which, btw, is a perfectly valid issue to be having as you consider
re: [iPhone} Exception After Creating First Object
I got a couple of private messages about breakpoints and stacktraces, which, of course, I had done before I posted my question. That's how I discovered that [mod processPendingChanges] led to an exception when adding to an empty database table. I learned a little more about the exception. It occurs in [self.tableView endUpdates] which is invoked, I think, as a result of [moc processPendingChanges]. I have had problems in the past with [self.tableView endUpdates] with empty database tables but thought that this time was different. Previously, I had tried preventing [self.tableView endUpdates] being called when the database tables was empty but that caused other problems (e.g. my Add Entity Name button didn't appear when the database table was empty). I've seen others mention problems with [self.tableView endUpdates]. Does anyone know how to make [self.tableView endUpdates] less vulnerable when the associated database table is empty? Steve, Which version of iPhoneOS SDK are you building for ? This sounds like an issue that was fixed in 3.1. There has been more discussion of known issues and work arounds for the NSFetchedResultsController on the devforums. You might try searching them, or looking at https://devforums.apple.com/message/100783#100783 - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: CoreData async fetch request
On Oct 7, 2009, at 10:12 PM, David Melgar wrote: Hello, I didn't mean to state threads as a requirement when I said async, I just meant some way to get partial results, such as a call to a delegate I referenced in the previous note. And I'm certainly not seeking complexity of threads if I don't need it. There isn't a simple way to do this. There is, however, a simple way to make your queries (in both Core Data, and direct SQLite) 100x faster. In my case, == is not the query I need. I really want case insensitive match as I presumed LIKE would do, or better yet, 'LIKE foo%'. Great. The Derived Property sample project on ADC shows you exactly what you need to do for replacing LIKE foo or LIKE foo% In the meantime, I've just completed a test using SQLite. The query in Coredata using 'LIKE foo%bar' across roughly 8 million records didn't return anything until the query was done, and the query took 2min 14sec, This runs through ICU, and cannot use an index. It can trivially be made 100x faster, at least for equality, prefix, and suffix matching. What's the actual SQL Core Data logs ? Doing what I interpreted to be equivalent using SQLite across 20 million records returned in 11 seconds What was the actual SQL you believed equivalent ? and I was able to get my first result almost immediately without needing threads. They provide a callback scheme, analogous to calling a delegate in Cocoa. FYI, this is across 20 different databases, otherwise it may have been faster. For what I'm doing, SQLite seems much faster, smaller footprint and easier to comprehend. I don't understand what I'm losing. In your note you mention the cost of doing unicode aware regex and sorting. As far as I know, I don't need any of those, at least not yet. All of your data is 100% 7 bit ASCII ? None of your customers will be Asian, European, or Hispanic ? None of your English customers will expect Unicode data that they copy and paste into your application from Safari to be preserved correctly ? If your answer is no, then you'll need to implement your own string matching operations for SQLite using either CFString or ICU. SQLite's built in operators use memcmp(). Once you've done that, I'd love to see the performance results. I took a brief look at the derived property example. Seems complex. What about the example seems complex ? Everything you need is in 1 file, that's got 107 lines of code. You can just copy and paste that code. Make a searchText column that has preprocessed the text data into a simpler form that can be used with an index and a simpler query. You say you want to do a case insensitive search across 20 million rows. You could absorb the cost of that, relative to a simple binary bitwise compare, for each of the 20 million rows. And pay it again every time you execute a query. Or, you could store a preprocessed column, and absorb that cost for just 1 string, the current search term foo Perhaps for ASCII case insensitivity, that doesn't seem particularly interesting. But once you start dealing with real world text encodings, which every Mac OS X and iPhone OS customer expects from every app, this becomes a big deal. Unicode is complex and difficult to work with. Something the framework should give me an option to handle rather than me having to generate such complex code. Does it advocate creating another field in the database as a normalized version of the field I really want to search on? Yes. Why would this be better than using SQLite directly? Doing this in SQLite directly will also be 100x faster than what you're doing now. Regardless of whether or not you decide to use Core Data, this would be better. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Core Data KVO with To-Many Relationships
Since my previous post, I have been able to get the functionality I was hoping for with the following code in my Department subclass. I would greatly appreciate some feedback as to whether this is an appropriate way to implement the functionality or if there is a more efficient or cleaner way. KVO doesn't have an easy way to observe a collection's contents, so the NSNotification approach is your best bet. - (void)awakeFromFetch { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(managedObjectContextDidChange:) name: NSManagedObjectContextObjectsDidChangeNotification object:[self managedObjectContext]]; } - (void)awakeFromInsert { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(managedObjectContextDidChange:) name: NSManagedObjectContextObjectsDidChangeNotification object:[self managedObjectContext]]; } Most people use a 3rd object to do this instead of having the individual managed objects. You can have an innocent bystander observe the NSManagedObjectContextObjectsDidChangeNotification, see if any interesting objects have changed, and then ping them to recompute whatever you need. Much easier to manage registering and unregistering observers. - (void)managedObjectContextDidChange:(NSNotification *)notification { // Get a set containing ALL objects which have been changed NSSet *insertedObjects = [[notification userInfo] objectForKey:NSInsertedObjectsKey]; NSSet *updatedObjects = [[notification userInfo] objectForKey:NSUpdatedObjectsKey]; NSSet *deletedObjects = [[notification userInfo] objectForKey:NSDeletedObjectsKey]; //this method of gathering changed objects is because the final set was always null if the first set was null NSSet *changedObjects; if([insertedObjects count] 0){ changedObjects = [insertedObjects setByAddingObjectsFromSet:updatedObjects]; changedObjects = [changedObjects setByAddingObjectsFromSet:deletedObjects]; }else{ if([updatedObjects count] 0){ changedObjects = [updatedObjects setByAddingObjectsFromSet:deletedObjects]; }else{ changedObjects = [NSSet setWithSet:deletedObjects]; } } if([changedObjects intersectsSet:[self employees]]){ //if an employee in this department changed, indicate that the totalSalary attribute should be refreshed [self willChangeValueForKey:@totalSalary]; [self didChangeValueForKey:@totalSalary]; } } That's the basic idea, but it's easier and faster to do that once, in one master observer, than in each managed object. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: CoreData async fetch request
On Oct 8, 2009, at 8:28 PM, David Melgar wrote: I read a little on ICU and now understand that sqlite by default does not handle case insensitive unicode. Is there an easy way to make sqlite use ICU on the Mac, or do I have to build it myself with ICU enabled? Probably the easiest thing to do (besides just using Core Data) is register custom functions with the SQLite API that go through CFString. The SQLite APIs for this are fairly straight forward. The ICU dylib on Mac OS X is not public API. There are a variety of references to building ICU or otherwise addressing this issue on the web. Should you do that, you can register custom functions leveraging ICU with SQLite as one would using CFString. I'd strongly recommend against building and statically linking SQLite yourself. Based on the derived property example, it seems that I would need to duplicate any text fields I commonly search on where I want to support case insensitive queries. Yes, for data sets of this size. For smaller data sets, in the 10^2 or 10^3 range, you might decide to skip this and accept slower queries since they'll probably be around 5ms - 20ms. How does spotlight manage to index so much text and respond quickly? Are those queries can insensitive? Spotlight supports insensitive queries. Spotlight also duplicates much of the material for its index. The importing process is not free. You're certainly welcome to put all your data into Spotlight and test its query performance for your purposes. Spotlight is fundamentally a full text search index. It's optimized heavily for prefix and word searching across very large numbers of documents. It's very good for its intended usage pattern, but that pattern is not the same as the usage of relational databases. The technologies are complementary. I've done a bunch more performance tests. I don't understand some of the differences. I hadn't realized that coredata in the data model has a flag to index a field. When I did that for Coredata, it dramatically sped it up. Although adding an index to my sqlite test slowed it down. Your test results are very odd, in that they are still 50x to 100x slower than I would expect. These tests have not yet applied the derived property optimization I suggested, correct ? As literally noted here, none of those queries are eligible for an index, so I assume your actual code is doing something slightly different, or the timing difference are due to configuration issues. Hot I/O and cold I/O will be very different. For example: search, without an index (cold I/O) add an index (page everything into the UBC cache) search, with an index, but a query that doesn't use the index (hot I/O) The difference between hot and cold I/O for query performance will be huge. As much as 100x. You can use /usr/bin/purge to force everything to be cold I/O. Not necessary representative of real world performance, as the whole disk cache will be flushed, including material relevant to the system that would normally exist when you run your app. Coredata, without an index, predicate name like foo, 20 databases 400k records each. 2m17s. Coredata with an index, predicate name like foo, 20 databases 1mil records each, 25 seconds. SQLite no index, sql where name like foo%bar, 20 databases 1mil records each 11 seconds. SQLite index, sql where name like foo%bar, 20 databases 1 mil records each 30 seconds. Why the 20 database files ? These queries will be faster with a single database file and its unified index. In any event, the derived property optimization should make all the prefix/equality queries subsecond. On my laptop, for 20 million records, that should be in the 500ms ballpark (hot I/O) As you said, with an index, and testing for equality, both coredata and sqlite queries responded in 1 sec. SQLite without an index was 9 seconds for same query. That sounds about right. It'll be faster if you use a single database with 20 million rows and a unified index table. I can only presume SQLite slowed down with an index because the database file was physically much larger and took longer to read from disk. I suspect its a flaw in the testing methodology, actually. Do you run the queries multiple times, throw out the best worst and average the rest ? Are you running other apps at the same time you do performance tests ? It is possible for the presence of an index to negatively impact a non- indexed query, but that would be a few %, not 3x slower An indexed query would read many fewer pages from disk than a non-indexed query, regardless of the file size. O(lg(N)) instead of O(N) and at 20 million that's a big difference. There are other possible issues in play, but you said the result set is small, so they seem unlikely. - Ben ___ Cocoa-dev mailing list
Re: CoreData async fetch request
On Oct 6, 2009, at 8:29 PM, David Melgar wrote: Hello, Thanks for the response. Seems that its straying somewhat from my original question. Sure, your original question is that you have a serious performance issue, and you'd like to hide it from the user by adding threads. I'm proposing fixing the performance issue instead, and not bothering with the additional complexity of threads, at least until you have 100 million rows or so. For the 1.4 million row db I have handy, the indexed == query runs over 100x faster than the LIKE query. == returns 4 rows out of 1.4M in 4ms and LIKE returns 4 rows in 450ms. So, on my 2007 Mac Pro, your 10 million row database would run in its query in less than 100ms. Too fast for meaningful human perception. Do we really need to add threads for this ? The code to incrementally and asynchronously display the results will probably take longer than Just Do It. Searching based on prefix matching is fine. The predicate I'm using really is of the form SELF like foo, no wildcard, so it doesn't seem that it should be that expensive. Locale aware Unicode regex is very expensive. Unicode is the worst possible text encoding system ever conceived, except for the others. Core Data insulates you from this so that your searches behave like OS X customers around the world expect. You're welcome to learn all about Unicode and ICU, and work with it directly in SQLite if you prefer. It'll take a lot of code to make searching and sorting work for every locale. You say its possible to structure this to use a binary index. How? I don't see any mention of indices in the Coredata documentation. See the Derived Property example on the ADC web site that I've referenced repeatedly. However, if you're not using any wildcards, and your search is case sensitive, then you might as well just use == and be done. Be sure to add an index to the attribute in your model. If I use SQLite directory, presumably I can set indices on the fields I want and more closely manage the data model. You would presume incorrectly. Generally, LIKE queries are not eligible for indices. There are some special circumstances where they can be, but that won't work with Unicode. You're welcome to verify that for yourself. I don't see how setBatchFetchSize helps. Doesn't it just limit the number of results returned? No. It's more closely an in memory cursor. It will require the entire WHERE clause execute, which unfortunately is your primary problem, but it will not restart the query as you stream through the results. I have no idea how quickly the results will come in. Setting a size 1 is therefore indeterminate and may take the full 3 minutes. If I set it to one, and I want to try and get the second row as well, it appears that it starts the query all over again, worst case resulting in 6 minutes before the 2nd result shows up. Doesn't seem that it scales reasonably if I want to display the first 10-20 entries. No, -setFetchBatchSize does not restart the query. That's what using fetchOffset does (in the database, not Core Data, which is why we wrote fetchBatchSize ourselves) My issue with Coredata is that it NSFetchRequest always returns ALL the results of the particular query at one time. If I use SQLite directly... assuming it supports cursors, I can get each result one at a time as they show up, display it to the user without slowing down the query as it continues to find other results. If you try using -com.apple.CoreData.SQLDebug you will see both the SQL we pass to SQLite, and some performance annotations like: 2009-10-07 17:52:15.107 Address Book[13949:5403] CoreData: annotation: sql connection fetch time: 0.0013s 2009-10-07 17:52:15.108 Address Book[13949:5403] CoreData: annotation: total fetch execution time: 0.0020s for 14 rows. The first line is how much time was spent in SQLite. If you run this with your text queries, you'll see most of your time spent there. Switching to use SQLite directly is not going to change that. Again, you should verify that for yourself. NSFetchRequest could support a delegate to invoke some method when for each item that has been found, rather than blocking until all the results are received. It also could have been implemented as a virtual queue, an object which could be read from while being written to in another thread. That would make an excellent feature request. Please file it with bugreport.apple.com But if you take my advice and make the query run in 1.8s instead of 180s, how important is this to you ? - Ben On Oct 6, 2009, at 4:08 AM, Ben Trumbull wrote: On Oct 5, 2009, at 7:00 PM, enki1...@gmail.com wrote: I am doing a simple query search for a text string pattern (ie 'SELF like foo') on ~10 million small records stored persistently using sqlite. This is a performance test to make sure I get
Re: CoreData async fetch request
On Oct 5, 2009, at 7:00 PM, enki1...@gmail.com wrote: I am doing a simple query search for a text string pattern (ie 'SELF like foo') on ~10 million small records stored persistently using sqlite. This is a performance test to make sure I get reasonable performance from my database engine before I commit too much code to it. Well, @self like 'foo' is a different problem than @self like '*foo*'. LIKE queries require Unicode compliant regex and are intrinsically expensive. If you do not have a wildcard, you are better off use an == query. The DerivedProperty ADC example shows how to transform the text to make it much faster to search. If you do need to use a wildcard, you'll really want to stick with 1, either prefix matching or suffix matching. The DerivedProperty example shows prefix matching. It's possible to structure this to use a binary index, and make the query extremely fast even for millions of records. There is a huge difference in computational complexity. Prefix matching can use an index, and therefore can run O(lg(N)). *foo* (contains) searches are slow, and cannot use an index. You really want to avoid these. Even Spotlight does not do arbitrary substring matching. Compare help with elp in your Spotlight results. If you want word matching, you can use Spotlight or SearchKit to build a supplemental FTS index. The query is taking over 3 minutes with a small result set. This is on a new 13 macbook pro w 4gb memory. ... a full table scan executing a regex on each of 10 million rows on a 5400 rpm drive ? Well, for doing all that, 3 minutes sounds pretty fast. Just as a reference point, if you grab the objectIDs from the result set, and execute an IN query selecting those objects, how long does it take ? 50ms ? 100ms ? The query is taking too long for a user to sit and wait for it. Is there a way to speed it up? Can indexing be applied to it? I had thought if I could display results as they are found that might be reasonable. In my tests, if I use setFetchBatchSize and setOffset to restart it, then it ends up repeating the query taking that many times longer to get a result. Not reasonable. It does not seem to start the query where it left off, as a database cursor would do. You can use -com.apple.CoreData.SQLDebug 1 to see the SQL we pass to the database. This also has nothing to do with Core Data. This is how offset queries behave. I realize it's not what you expected, which is why I recommended using -setFetchBatchSize: instead. My impression is that my usage scenario is not an appropriate use of core data. Core Data is just passing the query off to the database. I'm not sure why you think going to the database directly will do anything for the 179.9 / 180.0 seconds it takes to evaluate the query in the database. I was planning to try SQLite directly. Would it be more appropriate? You can try it directly, but it won't have any meaningful effect on your performance results except that SQLite's built in LIKE operator doesn't support Unicode. It'll be a tiny bit faster for that, but still the same order of magnitude. And then, either you'll have to integrate ICU support as Core Data does, and it'll be exactly the same, or be stuck with ASCII. Regardless, you'll need to make your searches eligible for an index. The DerivedProperty example shows how to do that. - Ben Thanks On Oct 5, 2009 7:14pm, Ben Trumbull trumb...@apple.com wrote: Is there a way to do an asynchronous fetch request against Core data returning partial results? That depends on whether it's the query part that's expensive (e.g. WHERE clause with complex text searching and table scans) or simply the quantity of the row data that's your problem. For the latter, you can just use -setFetchBatchSize: and be done. You can use a separate MOC on a background thread to perform asynchronous work. You can then pass over results to the main thread to display to the user. However, unless your search terms are very expensive, it's usually easier and faster to use - setFetchBatchSize: synchronously. For well indexed queries, it can handle a million or two rows per second. Not sure why you'd subject your users to that kind of experience. It's common to use fetch limits, count requests, and only show the top N results. What's your user going to do with a hundred thousand results anyway ? If you need to attack the computational expense of your query terms, that's more complicated. Obviously it would be best to optimize the queries and ensure they are using an index. But if that's not enough, you can execute the queries in a background MOC, fetching objectIDs + row data (put in the the row cache) and then have the other MOC materialize the objects by ID from the row cache. There's a BackgroundFetching example in /Developer/Examples/ CoreData. It shows how to do
re: CoreData async fetch request
Is there a way to do an asynchronous fetch request against Core data returning partial results? That depends on whether it's the query part that's expensive (e.g. WHERE clause with complex text searching and table scans) or simply the quantity of the row data that's your problem. For the latter, you can just use -setFetchBatchSize: and be done. You can use a separate MOC on a background thread to perform asynchronous work. You can then pass over results to the main thread to display to the user. However, unless your search terms are very expensive, it's usually easier and faster to use -setFetchBatchSize: synchronously. For well indexed queries, it can handle a million or two rows per second. Not sure why you'd subject your users to that kind of experience. It's common to use fetch limits, count requests, and only show the top N results. What's your user going to do with a hundred thousand results anyway ? If you need to attack the computational expense of your query terms, that's more complicated. Obviously it would be best to optimize the queries and ensure they are using an index. But if that's not enough, you can execute the queries in a background MOC, fetching objectIDs + row data (put in the the row cache) and then have the other MOC materialize the objects by ID from the row cache. There's a BackgroundFetching example in /Developer/Examples/CoreData. It shows how to do this. Returning partial results incrementally would require some creativity on your part to subdivide the query into several. Since most expensive queries are text searches, it's usually possible to subdivide the result set naturally. Like the first letter of 'title'. Similar to the thumb bar index on the side of the Contacts app on the iPhone. There's also a DerivedProperty example on ADC for optimizing text queries. Obviously, Apple's own Spotlight could not use something like Coredata, since it heavily relies on returning asynchronous partial results. Which is neither here nor there. Most Cocoa applications wouldn't want Spotlight to be the sole persistence back end of their data. The latency of putting all your data in a full text index instead of a relational database or keyed archive would be pretty absurd. Now, if you're writing an app that's primarily structured around full text searching, you might instead prefer to focus on putting your data in Spotlight via small files, and using the Spotlight APIs. But it's not suitable for apps interested in an OOP view of their data. Frankly, this is my second application I've attempted to use Coredata to find it come up surprisingly short. The first time the issue was core data not being thread safe. Core Data can be used efficiently with multiple threads. It might help to think of each MOC as a separate writeable view. If you'd like to know more, you can search the archives for my posts. What is the target market for Core Data? Why sort of application is ideal for its use? What size data store? Right now it escapes me. Cocoa and Cocoa Touch applications, particularly done in an MVC style with an OO perspective on their data. Some people also use it as a persistent cache for data stored in another canonical format, such as XML files. On the Mac side, we've had customers with 3+ million rows (multi GB) databases, and on the embedded side, roughly 400,000 rows (100s MB). However, it does take some care and feeding to handle data sets like that, and most developers find it straight forward up to about 10% those numbers. It sounds like you're having performance issues. What kinds of queries are you trying to accomplish ? How much data are you working with ? How have you modeled your primary entities? You can fetch back just NSManagedObjectIDs, and - setIncludesPropertyValues: to NO to effectively create your own cursors if you prefer. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: whether to use core data...
But the Core Data documentation starts like this: ... Core Data is not an entry-level technology. ... You should not simply try to read [The Core Data Programming Guide] straight through to understand Core Data. ... Do not attempt the NSPersistentDocument Core Data Tutorial unless or until you also understand Cocoa bindings. ... Although Cocoa bindings and Core Data are independent and address different issues, both provide abstraction layers that˜while individually they are reasonably straightforward to grasp˜can be challenging to master simultaneously. Bloody hell! WARNING! Do not even ATTEMPT the NSPersistentDocument Core Data Tutorial! Your very MIND is in MORTAL DANDER! Ironically, the Low level Persistent Store tutorial was a lot easier for people new to Cocoa. I believe it's been replaced by http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/CoreDataUtilityTutorial/Articles/00_introduction.html The warning is a touch harsh. Still, it's there because the Core Data abstractions are built on top of the other foundational Cocoa elements. If you don't get KeyValueCoding, you're going to be lost. It wouldn't be doing you any favors to suggest otherwise. Ditto for Cocoa collection classes, KVO, and NSNotificationCenter. The NSPersistentDocument tutorial is more difficult because it's at the intersection of several different technologies. NSDocument from AppKit, Cocoa Bindings, and Core Data. So that's learning 3 separate things at once, and it's rarely clear to people new to Cocoa where those technologies intersect, and where they are distinct. It'd be like learning how to whistle, juggle, and snap all at once. Could you ? Sure. But experience has demonstrated it's easier and significantly less frustrating to learn them separately. Or put another way, some of the tutorials are intended for advanced Cocoa developers new to Core Data, not developers new to Cocoa. Try the Core Data Utility Tutorial. It's a CLI, so you don't have to get wrapped up in NSViews or Cocoa Bindings as you experiment with Core Data. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Strange Core Data save behaviour (required relationship nil... when it is set the line before saving)
On Sep 30, 2009, at 12:56 AM, Luke Evans wrote: Well, I'm more than happy to file a bug, as it has been tricky to figure out (and I would probably still be at it without your interjection). There are several ways to frame the problem of course: it could be a documentation bug... things aren't as simple as might first appear in the docs/guide, or maybe something can be done to have deleted object behave in a 'friendlier' manner w.r.t. their defunct relationships. I suppose I can just find a general form of words and let you good folks figure out what it really means in practice :-) Probably some of both. I still think I might have something more to figure out here too. At the end of my testing, as an experiment I had a main thread timer fire periodically to perform a save on the main thread's MOC (without performing any changes on the main thread's MOC at all). This induced the same problem, and I'm still curious as to how the main thread's copy of the graph might have the nil in the relationship under these conditions. AFAICS there shouldn't have been any chance for either MOC to be in this condition at any time. I assume the merge operation from the other thread is 'atomic' somehow and activity on another thread (like a save) should not be able to catch that MOC in some kind of in-between state? merge ? I'm not sure I have a full grasp on your work flow here. If you call mergeChangesFromContextDidSaveNotification, then that can obviously make changes to the object graph. There are two other issues in play. First, if you've set a merge policy, then the MOC may pull in changes necessary to make the save correct (e.g. implement and correct an optimistic locking failure). Second, firing a timer on the main thread is totally non-deterministic with respect to anything else on the main thread. The application event loop is rather amorphously defined, so timers can fire either inside or outside the main thread's current event's undo grouping. Timers are intrinsically very unpredictable. Aside from this 'stress' test though, I haven't (yet) got it to fall over - essentially under the conditions where only one thread (albeit one of several threads on any occasion) is making changes and saving at a time. I'm not sure I understand this last comment. Do you have threads sharing a MOC ? Because threads with their own MOCs can make their own changes and own saves simultaneously. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Strange Core Data save behaviour (required relationship nil... when it is set the line before saving)
I don't think anyone has cared enough to file a bug on this. I don't get it. There's an open manhole in the street with the manhole cover lying right next to it, and the problem is that no one cared enough to call the Department of Works to complain? This has come up 3 or 4 times in about 6 years. So, is it an open manhole in the middle of the street, or is it that the unpaved road behind your house that nobody uses is missing a street sign ? A quick glance suggests this should be pretty easy to fix, but that the obvious fix will be slower. Should everyone's deletes get slower just to spare Luke ? Maybe. Hard to say without quantifying it. So, now some performance tests need to get written, and the results measured. Also, there's no bug report, and no developer provided test case to verify the fix. So that needs to get written too. Should we spend our time on this or, judging by complaints, something more important ? cocoa-dev is not a bug reporting forum. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Strange Core Data save behaviour (required relationship nil... when it is set the line before saving)
Now, I have some code that changes the value of the 'B enumeration value' that A is using. This does the following: 1. Create a new instance of the B subentity that represents the value we want (in the same MOC as A) 2. Delete the old B object that A was pointing to, i.e. [moc deleteObject:B]; 3. Set A's to-one relationship to point to the new B object (and for good measure, set B's inverse relationship - though this should be done automagically). 4. Save the moc 4. is where badness happens (failed to save). The error tells me that A's relationship property to B is nil... but just before I do the save I log the value of the object referenced by this relationship and it's the new 'B' object! I have no idea what I've done to upset Core Data such that it claims a relationship is nil when I save, but the line before the [moc save:err], the relationship shows as referencing a perfectly good object. So you delete B, which has an inverse relationship to A. Then you set a new B on A. Then you save, and delete propagation cleans up the graph, nullifying the old B's inverse relationship ? What happens if you add a call to -processPendingChanges in between #2 and #3 ? - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Strange Core Data save behaviour (required relationship nil... when it is set the line before saving)
On Sep 29, 2009, at 8:22 PM, Luke Evans wrote: Hello Ben. What happens if you add a call to -processPendingChanges in between #2 and #3 ? ... well then everything works wonderfully (oh joy!!) :-) OK. I need to get a proper mental picture of why this is needed in this case. I guess I was vaguely aware of this method from previous passes though the Core Data docs, but... - The method documentation itself doesn't _really_ suggest it may be essential in some cases. Rather, the talk is about getting the undo manager into step, and even then the statement is made that this is done by default at the end of the run loop. - deleteObject docs, or indeed the guide section on deleting (Creating and Deleting Managed Objects) makes no mention of a need to call this method - I had tried manually setting the old deleted objects 'back relationship' to nil, before deleting it, and before setting A's relationship to the new B. This hadn't worked, but was my attempt to keep the relationships consistent - at least in in the MOC that induced the change. It's tempting to just think that you should _always_ do a - processPendingChanges before a -save:, but I'd prefer to understand what's really happening here. It's not before the save. It's in between the deletion and the re- assignment of the relationship of the surviving object to a new object. The problem is reassigning the relationship before delete propagation runs. Delete propagation, as well as the change notifications and several other aspects of object graph change tracking are coalesced and run later. Calling processPendingChanges is one of those later times. The application event loop also calls it, which is the default timing. Executing a fetch, or a save will also call it. Manually setting the deleted object's relationship instead of calling processPendingChanges between steps #2 #3 should also work. I don't think anyone has cared enough to file a bug on this. - Ben If you have insights on the above, then that would be great. Regardless, you've just improved my humour by several degrees ;-) -- Luke On 2009-09-29, at 3:59 PM, Ben Trumbull wrote: Now, I have some code that changes the value of the 'B enumeration value' that A is using. This does the following: 1. Create a new instance of the B subentity that represents the value we want (in the same MOC as A) 2. Delete the old B object that A was pointing to, i.e. [moc deleteObject:B]; 3. Set A's to-one relationship to point to the new B object (and for good measure, set B's inverse relationship - though this should be done automagically). 4. Save the moc 4. is where badness happens (failed to save). The error tells me that A's relationship property to B is nil... but just before I do the save I log the value of the object referenced by this relationship and it's the new 'B' object! I have no idea what I've done to upset Core Data such that it claims a relationship is nil when I save, but the line before the [moc save:err], the relationship shows as referencing a perfectly good object. So you delete B, which has an inverse relationship to A. Then you set a new B on A. Then you save, and delete propagation cleans up the graph, nullifying the old B's inverse relationship ? What happens if you add a call to -processPendingChanges in between #2 and #3 ? - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Snow Leopard, core data, read only and multiple threads
I've got an app that worked on Leopard. I ported it to Snow Leopard SDK 10.6, and now it works on Snow Leopard, but it doesn't work correctly on Leopard anymore. I haven't changed anything that ought to affect this. What doesn't work ? It's an app with a foreground gui that writes an XML coredata store. A background thread reads the repository and takes action. Both threads have the full core data stack with their own coordinators. As soon as I activate the background thread, the XML store gets set to zero bytes. The XML store is an atomic store. Everything is loaded at once, and everything is written out for each save. Very NSDocument like. Like TextEdit. Two people open up Text Edit, pointed to the same path mounted over a shared volume. What happens ? You almost certainly want to use the SQLite store, or have the stacks work with different XML files. When I encountered the problem I read the doco and I added the NSReadOnlyPersistentStoreOption when calling addPersistentStoreWithType in the background thread, but that hasn't helped. It wasn't necessary before. NSReadOnlyPersistentStoreOption doesn't have anything to do with multi- threading. You sure you're not saving a MOC ? - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Re: [__NSFastEnumerationEnumerator nextObject] unexpectedly not very fast!
In summary, the existence of fast enumeration does nothing for existing enumeration technologies and if you have to support 10.4 (as I do) you simply can't use it unless you fork your code. My solution, in the few cases where performance is paramount, has been to essentially roll my own fast enumeration. For very large arrays (thousands of objects) I'll get blocks of objects in batching using [NSArray getObjects:range:], process those in a tight C loop, and then get another batch. The for (in) construct is by far the most optimized general purpose way to work with any of the collections in Cocoa on 10.5, 10.6, and iPhone OS. It's also polymorphic, so custom collection subclasses can fine tune the behavior. If you have to hand roll your own, either for 10.4, or some specialized case, your best bet is to use -getObjects: into a local buffer. gcc supports variable length arrays, and that's the fastest way to marshall these batches temporarily. You'll want to be very careful to do length checks to not overrun your stack, and limit the size. If you have somewhere between 256 512 objects, give or take, you'll have to malloc a temporary id* Overflowing the stack is not pretty. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Core Data: relationship 'too large' when saving
I am encountering an error that I have not seen before. While saving in a NSManagedObjectContext I am encountering an error that reports a to-many relationship as 'too large'. This relationship has ~10,000 members but each member is relatively simple consisting of a few short strings and numbers. I have no problems operating on this relation; the error only occurs while saving. Many GB of disk space are available. Error occurs independent of store type, SQLite, binary, or xml. Smaller (different) relations of ~1000 members are correctly saved. 1) Is this really a size problem? No. I suppose it's possible there's a 16 bit overflow bug somewhere, but there shouldn't be (obviously), and I can't imagine any issue with 10,000. It is possible for you to set the maximum size of a to-many relationship in your model. Did you check the property validation rules for this relationship ? 2) can someone point me to relevant documentation? You sure it's not NSValidationNumberTooLargeError for one of the numbers being outside the range you specified acceptable in the model ? - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Core Data memory not freed after reset
On Sep 22, 2009, at 8:54 AM, Sean McBride wrote: On 9/21/09 4:21 PM, Ben Trumbull said: If you're using an NSArrayController in Entity mode, you can turn on Use Lazy Fetching. You'll want to disable auto-rearrange content. Ben, May I ask, why turn off 'auto-rearrange content'? Is it not compatible with 'use lazy fetching'? Or does 'auto-rearrange content' on its own degrade performance somehow? auto-rearrange content is very expensive. Preserve selection can also be expensive, although not nearly as bad. But if we're talking about a million object table view (which is a little odd, btw, most UIs have ... and more instead) then extraneous layout options will add up fast. I have a vague and hazy memory that auto-rearrange content is not compatible with use lazy fetching. If you run into trouble, file a bug, and disable auto-rearrange content as a workaround. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Core Data memory not freed after reset
in my SQLite backed Core Data app, a search action fetches from a large number of objects (1.000.000) only to show them in a table. When the user exits search mode (search string empty), I'd like to free the managed objects to restore the app's normal memory footprint. I do that by resetting the managed context, but it doesn't seem to work, physical memory stays where it was when the fetch was completed. Strangely, in ObjectAlloc instrument I can see the fetched objects being deallocated, but the physical memory still peaks. If ObjectAlloc shows the fetched objects being deallocated, but top shows a large RSIZE, and the heap command shows a very large heap thats most unused, then you're doing everything you can on the deallocation side. There is a difference between heap size, and VM allocated address space. This issue is caught at the boundary, where the malloc system won't aggressively deallocate address space after the memory using it has been deallocated. It doesn't, because as a general rule that's a net loss. So your only choice is to either (a) not worry about it or (b) reduce peak memory. (b) reducing the heap high watermark is a separate problem than simply freeing all the memory you allocate. Reducing peak memory will have many other performance benefits besides making your RSIZE look pretty. If you're using an NSArrayController in Entity mode, you can turn on Use Lazy Fetching. You'll want to disable auto-rearrange content. This works on 10.5. On 10.6 and iPhoneOS, you have more options, including using Batched Fetching on the fetch request with - setFetchBatchSize. This will make a vast reduction in peak memory when working with such a large result set. Working with 1 million objects like this will take ~16MB of RAM. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Core Data memory not freed after reset
Core Data has (or, I should say, had, since I haven't investigated the behavior in Snow Leopard) its own internal in-memory cache of object and attribute data, which means that, up to a point, data from a persistent store is in memory twice. AFAICT there's no way of unloading or controlling this cache, which has a maximum size that Core Data chooses. So you can get back the memory occupied by the objects themselves (and their attribute value objects), but your memory footprint could stick at a couple of hundred MB. You could just ignore the issue (the footprint won't grow beyond certain point, so it's sort of harmless), or try some of the fetch performance optimization techniques described in the Core Data documentation to see if you can keep unwanted information out of the cache from the beginning. The caching is happening at the NSPersistentStoreCoordinator level, and is shared copy-on-write with the managed objects themselves (e.g. faulting the same object in multiple MOCs will reuse the same string data). The raw column data is not duplicated in memory. The cache entries are deallocated after the managed object representing that row is deallocated, and at certain other times involving faulting. Simply releasing the last managed object representing that row is enough. You may need to call -processPendingChanges to purge anything that as accumulated as ready to be disposed to encourage this to happen sooner if you find it's taking too long. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Core Data threading fun
I have a server app that responds to network requests, making use of a Core Data database to serve responses. Some requests update the database. I have chosen to allow requests to arrive on multiple threads, and intend for these threads to use Core Data directly. In keeping with Core Data's doc related to threading, I have one Managed Object Context per thread, and these all share a common Persistent Store Coordinator that is managing a SQLite data file. It's my understanding that this scenario is an acceptable configuration, indeed it appears in the Core Data notes on threading as the 'preferred option' (q.v. http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/CoreData/Articles/cdMultiThreading.html ). OK, here comes the problem (!)... One type of request can change the name of an object. The handler for this request (using its thread's own MOC): 1. Fetches the required object by persistent ID (i.e. stringified NSManagedObjectID). Why stringify it ? NSManagedObjectIDs are immutable and you can just pass them between threads. 2. Changes the value of the 'name' property on the fetched object 3. Commits the change by calling 'save' on the MOC Unfortunately, while this works some of the time, I have a situation where a subsequent other request (possibly on another thread) sees the old name of this object. This request gets 'all' the entities of this type and sends properties (such as name) back. You can merge the changes into that context explicitly, or refetch the objects. Should I have a PSC per thread too? If so, will they behave correctly talking to the same SQLite data file? With a shared PSC, should I lock this whenever a write is being performed (at least)? Per-thread PSCs doesn't sound like what you want. You probably want the simpler shared PSC configuration for now. When you share a PSC between threads, the key point is to lock it whenever you message that PSC directly yourself. Typically, that's adding and removing persistent stores. It's easier to do that at init time, and leave the configuration stack after you start spawning threads. Core Data assumes responsibility for locking the PSC if we ever send ObjC messages to it. 2. Locking the MOC There is talk that locking the MOC, even one that is private to a thread, will engender 'thread friendly' behaviour in the PSC: ... Typically you lock the context or coordinator using tryLock or lock. If you do this, the framework will ensure that what it does behind the scenes is also thread-safe. For example, if you create one context per thread, but all pointing to the same persistent store coordinator, Core Data takes care of accessing the coordinator in a thread-safe way (NSManagedObjectContext's lock and unlockmethods handle recursivity). ... Should I lock the MOC, or just the PSC (see 1)? Would this really fix my experience of changes not appearing on other threads anyway? No, if you find yourself locking MOCs, you're almost certainly doing something wrong. 3. Changes propagating back to other MOCs looking at the same data I assume Core Data is smart enough to realise that an attribute value change in a Managed Object cached in one thread's MOC, should be reflected on (or at least invalidate old data in) another Managed Object instance representing the same data in another MOC. At the very least, I think I'd expect a new fetch request that has this object in the result set would cause data changed in the persistent store to show up properly on the object. We don't automate this step, although there is API to assist you. We try as much as possible to never change the state of your objects out from underneath you (e.g. push state). The reason for that is how complex things become when that local MOC has pending edits. Do we overwrite it out from underneath you ? Or not update it, but update other unchanged objects (seemingly randomly). There isn't a clear paradigm for coordinating these kinds of push changes between the framework and your code. So instead we follow something like a principle of least surprise mates with lesser evil. Basically, Core Data uses the poll model. State may change, but only because you did something to ask us to do that. Like refetch the objects, use a staleness interval, or call -mergeChanges... - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: How to create subentity object inheriting from superentity object in core data
On Sep 17, 2009, at 9:21 AM, Leon Starr wrote: So, going back to my example, (and the part everyone disagrees with!) I still don't get it. More importantly, my objc compiler doesn't get it! In the model, Auto.license is properly inherited by the Sedan and Truck subentities. No trouble at all with KVC interactions. But if there is no @property/@dynamic for license in my Truck subclass, (only in the Auto subclasss) I cannot access thisTruck.license without getting a compiler error. Which makes total sense to me since one NSManagedObject subclass (Truck) is not inheriting from another (Auto) in objc. They each inherit from NSObject. But I CAN do [thisTruck valueForKey:@license], which also makes perfect sense. This violates the rule that a subentity must use an Objective-C class that is the same as its superentity, or a subclass of its superentity's Objective-C class. If the Truck entity is a subentity of Auto, then it may reuse the AutoClass, or a TruckClass which must be a subclass of the AutoClass. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Core Data: strange keypath problem
Here is the console output: 2009-09-17 12:44:17.659 myAppSales[11094:a0f] Application DMXRef: janTotal starting... 2009-09-17 12:44:17.660 myAppSales[11094:a0f] Application DMXRef: janTotal predicates set. 2009-09-17 12:44:17.662 myAppSales[11094:a0f] anotherArray count = 117 2009-09-17 12:44:17.663 myAppSales[11094:a0f] anotherArray firstObject month = 5 2009-09-17 12:44:17.663 myAppSales[11094:a0f] keypath month not found in entity NSSQLEntity AppSales id=2 Any ideas would be greatly appreciated. Is 'month' marked transient on the AppSales entity in your model ? You cannot ask a *persistent* store to query or sort on *transient* data. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: How to create subentity object inheriting from superentity object in core data
I've got a generalization in my core data model with entities named A, B, C let's say where A is a super class with subentities B and C. A is not abstract, so if I create an A NSManagedObject, I need to create and relate a single B or C subclass object. How do I make this happen? I can create the entities, but HOW do I tell the model that object B is a subclass of object A (or vice versa?) Note: I did create the model programmatically and the subentities have been set properly for entity description A. Here's my sad attempt to move forward. As you can see, I've created the objects I need, but B doesn't know that A is it's superclass object. What to do? Model the entity inheritance in the modeling tool is the easiest approach. If you need to customize your model beyond that at runtime, you can make minor alterations programmatically by loading it and mutating it before creating your NSPersistentStoreCoordinator. If you programmatically create a model, you'll need to add all the super entity's properties to each of the subentities. The programmatic structure is much flatter than it would appear in the graphic Xcode tool. There's no calling super in entity inheritance. You'll need to set the objective-c class names for each entity. The objective-c class must be either the same as the super entity's class or a subclass of that super entity's objective-c class. You cannot create a random mapping of Objective-C classes to entity inheritance. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: How to create subentity object inheriting from superentity object in core data
Okay, my understanding, then, is that the inheritance is just in the model - makes sense. If you subclass NSManagedObjects for the parent and child entities, you need to explicitly declare and synthesize (dynamically) all common properties you want to access at lower levels of the hierarchy (at least). entity inheritance is just in the model. class inheritance still applies normally. Just to be clear, in the model I've got Entities: Auto, Sedan, Truck and the parent (Auto) has a common property license. Now when I get a pointer to a Sedan or Truck I want the ability to access via thisTruck.license or thisSedan.license. In fact, if I end up with a pointer (Auto *) thisAuto, I want thisAuto.license as well. To get this to work, without KVC, I need to declare @property/ @dynamic for license in Auto, Sedan and Truck. (Can't just put it in Auto and expect Sedan.license to work). No big deal unless I have a bunch of common properties in the parent entity. No, Objective-C properties are inherited by subclasses. If you use the modeling tools, your model will be created correctly, and your custom NSManagedObject subclasses will have the correct @interface definitions. Even if you need to do all this programmatically, I'd recommend to play with the modeling tools more to see what things are supposed to look like. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: CoreData multiple contexts and threads
Hello, I have a stream of data (some messages) from a socket and I need to save it in a CoreData db. I would to optimize this thing by using more than a managedobjectcontext for each N messages arrived. The problem is that I need to link these message between (parent/childs). How can I check if a message exist in a context without merge it with the main context? Need I to mantain an array of active contexts and comunicate with them in order to search parents and childs? If I merge each time with the main context it's a bit slow. Well, you can create the messages and save them in groups in their own thread and MOC, and link them up with their parents later. Or you can organize the background threads to own parent groupings and have those dedicate threads save their messages. The problem with inserting messages and relating them to parents haphazardly is that the to-many relationship on Parent will need to merge in the results on each save anyway, and having lots of threads banging on the same to-many will just engender a lot of merge conflicts. While the right merge policy will resolve them for you, that's not remotely free. You can look up a previously saved MO with - objectWithID: on your MOC. If you have a very large number of messages, you may not wish to model the to-many on Parent at all. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: CoreData Bug? (SQLite vs XML)
I've been trying to track down a bug and it *seems* that it might be CoreData's fault (I highly doubt it but there's a small chance). I have the following configuration: - A base class, let's call it Base - A subclass of Base, let's call it Subclass Subclass has a to-many relationship to Base. Here's what I'm observing: - On startup, the to-many relationship is not restored (only in some reproducible cases). Are all the objects correctly marked inserted or updated before you save ? Do all the objects report the correct inverses ? If you change relationships with setPrimitiveValue:forKey: or on MOs that haven't been inserted into a MOC or try to wire up relationships in -awakeFromFetch, then things can go awry. Do you have multiple MOCs saving ? The XML store is atomic, so the last writer wins. The SQLite store merges changes from other saves. Can you reproduce this in a new sample project ? - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Core Data Manual Migration
The process fails here and the log then contains the following: An error occured while manually migrating document: Error Domain=NSCocoaErrorDomain Code=134110 UserInfo=0xf336960 An error occured during persistent store migration. [6871:813] Error: { reason = Can't add source store; } There should be more in the userInfo dictionary explaining what's going on. If you can reproduce it in a new test project with your models mapping models, then please file a bug with the project source zipped up at bugreport.apple.com and we'll look at it. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Is Core Data appropriate to my task?
Gwynne, I have an application that manages two kinds of data: A singular file that contains a large amount of rarely changed (but not invariant) data, and documents that contain one root object's worth of information that connects to the singular data set in a very large number of places; the documents are in fact little more than a chosen list of references into the singular data set, plus some small bits of independant data. The documents are modified quite often. Originally I thought Core Data must surely be ideal for this sort of application; the object graph management alone should have been very helpful, to say nothing of the management of stores and the abilty to integrate with bindings and NSDocument. I got as far as reading all the basic (and some of the not so basic) Core Data documentation and began wondering whether or not my data would fit into its model after all. For example, in the large singular data set, there are a large number of what SQL would call lookup tables, data that's used only to avoid duplicating a set of constant values that are used elsewhere. To use the Employee/Department example from the Core Data docs, sometime in the future an Employee might have a planetOfOrigin attribute. Assuming one limits one's self to the restriction of the speed of light (so, not TOO far in the future), the resulting Planet entity would only ever have a small number of possible values. Such an attribute might be better modeled in the Employee entity by something like SQL's ENUM or SET types. If the set of possible values is Earth and Not Earth, a Boolean might make more sense. If the set of possible values is Earth, Mars, Venus, etc., an ENUM would be a reasonably obvious choice; after all, how often does the solar system gain or lose a planet (cough Pluto cough)? With such a small data set, a lookup table would only be the obvious choice if the set of possible values was expected to change with any frequency. But Core Data has no support for such a thing; I would either have to write a custom property type or model it by creating the Planet entity and giving it a relationship from Employee. Correct. You can write a custom NSValueTransformer with the Transformable property type to implement an ENUM, or normalize the data into a separate table as a formally modeled entity. Which is better depends on how big the data values are, how many of them there are, and how frequently they change. Is that really so bad ? The alternative is to do ALL the work yourself. Let's pretend the lookup table *was* the obvious choice for some reason; the speed of light barrier has been broken and now there's a whole mess of planets. So in Core Data parlance, the Employee entity has a one-to-one relationship to the Planet entity. A lonely planet. That's either going to be one-to-many or a no inverse to-one. The inverse relationship from Planet to Employee, all employees from this planet is technically feasible, even easy, to model, but it's almost certainly a waste of time and effort. But the Core Data documentation offers a long list of very dire warnings about one-way relationships between entities. Yes, and for most situations those warnings are there for very good reasons. But if there were no reasons for such relationships, then it wouldn't be a warning, it simply wouldn't exist. Worse, the list of possible Planets certainly doesn't belong in the same document file that holds a single Employee's data; you'd end up duplicating that data across every single Employee. So the list of Planets would instead be in a global store. There are lots of ways to model that, but, yes, this would be the most natural. But oops, Core Data can't model cross-store relationships, so you use a fetched property, which is one-way. You could use a fetched property, or handle this in code by storing a URI for the destination object in a different store, and fetching the matching objectID either lazily in in -awakeFromFetch. We've generally recommended using a custom accessor method for this instead of fetched properties. Inverse relationship problem solved, unless you actually had a use for that relationship. But fetched properties need a fetch request, and what do you put in the predicate? Now you need some kind of identifier in the Employee for the fetch to operate on, Yes, but this isn't any different than the problem would be without Core Data for managing values in two different databases. and now you have two fields (the planetOfOriginName string for the predicate and planetOfOrigin as the fetched property) to model a single relationship. How to maintain referential integrity? Again, no different than the problem would be without Core Data. This is why the modeling tool recommends using inverse relationships. Maintaining the integrity by oneself is tedious and error prone. And what if you DID want the inverse relationship - do you model another fetched
Re: Is Core Data appropriate to my task?
I don't see this as being equivelant at all. Extending the example, let's say the company with these Employees has as its directors several discriminating unfair people, and thus an Employee from any given Planet gets a salary adjustment based on that Planet. The obvious place for this data is the Planets table, or in Core Data's case, the Planet entity. A salaryAdj column (attribute) is added to the Planets table (Planet entity) and filled in with the (in)appropriate numbers. Now suddenly the company is taken over by far more benevolent and considerate people, whose only flaw is that they don't want to break a system that works by removing an entire column from a database table (a schema change is much more difficult than a data update, after all), so they just UPDATE Planets SET salaryAdj=0. Now you're conflating other issues. This is why I recommend not treating O/R systems as perfectly equivalent to databases. They're not. On Snow Leopard iPhone OS, you can make modest alterations to the Core Data schema easily. Just keep a copy of the old model, and pass the 2 keys to the options dictionary when you add the store to the PSC to leverage light weight migration. Core Data will infer the appropriate schema changes and adjust the schema in place (alter table style). http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/vmLightweight.html#//apple_ref/doc/uid/TP40008426-SW1 If you do make some unusual and radical modifications (split tables into multiple new tables, compose old tables into a single new table, etc), you can use the full mapping model migration. While it won't perform as well as light weight migration, at least you'll have tools support in handling the schema migration. So someone loads up an Employee whose Planet instances are in the same store with that Employee, and the old salary adjustment is still sitting there in the saved data. I sense unhappy Employees in this company's future. If only the coder who wrote the payroll system had put the Planet data in some global store where changes to it would propogate correctly to all Employees. If this is important, than you can use multiple persistent stores. I suspect Erik's point, though, is many apps don't have a significant issue with a small amount of duplication in the individual documents. Disk space is usually cheap. And being completely self contained has its advantages (perhaps not relevant to you, but still existent). global mutations is a double edged sword. What if your documents are loaded in a newer version of the app, but have some implicit data dependency on the older global data ? That can get messy. Does Core Data still solve the problem? Is there some reason that using Core Data for everything would be better than storing the global rarely-updated data in a real database and using Core Data only for the Employee entity, which is the only part which really talks to the UI anyway? (Something tells me the key point is right there...) For that matter, if Core Data is only managing one entity, what's the use of Core Data at all? With all the data being referential between the database and the entity, just define a simple NSObject subclass which contains a few instance variables and implements NSCoding. Then why not use Core Data for the database and for the entity implement a simple NSObject subclass with a few instances variables ... ? Although, it seems a little silly to not use Core Data for the simple part when you'll get persistence, change tracking and Cocoa Bindings integration for free. Most people find NOT maintaining backward compatible initWithCoder methods in perpetuity quite refreshing. I know one developer seriously considering rewriting their iPhone app for no other reason than to use Core Data's light weight migration and never hand roll another database schema upgrade again. Here's an excerpt from a post regarding when to use Core Data on the iPhone: I suppose I could tell you how great an addition to Cocoa it is, or how much TLC its performance tuning gets. But what I've seen our most sophisticated clients decide is that it saves them from writing a lot of code. The model code with Core Data is usually 50% to 70% smaller as measured by lines of code. Why reinvent that ? App developers don't get paid to write database code. Can you learn SQL ? Sure. Do your customers care ? No. App developers get paid for novel functionality that addresses a real customer need with good UI. - Ben Here's a more traditional reply: - Full KVC, KVO support out of box - Relationship maintenance (inverses, delete propagation) - Change tracking - Sophisticated SQL compilation - NSPredicate objects instead of SQL - NSPredicate support for correlated subqueries, basic functions, and other advanced SQL - Proper Unicode, local aware searching, sorting, regex
Re: Is Core Data appropriate to my task?
Before anything else, let me say thank you for a clear, concise, and very helpful set of answers to my questions; I was expecting rather more of a struggle for understanding :). my pleasure. On Sep 10, 2009, at 5:04 PM, Ben Trumbull wrote: The inverse relationship from Planet to Employee, all employees from this planet is technically feasible, even easy, to model, but it's almost certainly a waste of time and effort. But the Core Data documentation offers a long list of very dire warnings about one-way relationships between entities. Yes, and for most situations those warnings are there for very good reasons. But if there were no reasons for such relationships, then it wouldn't be a warning, it simply wouldn't exist. The manual isn't at all clear about this, but if I understand correctly, you're basically saying, Though it is almost always technically possible to model an inverse to any relationship, there are sometimes circumstances in which it is correct not to do so. Is that accurate, and if so, should I file a documentation bug requesting clarification on that and the circumstances in which it's true? Sure. It's a challenge to document some of this material in a way that steers most developers down the typically optimal path while still keeping advanced options open. We have many developers with little or no database experience, and we want to encourage them to use inverses until they have a compelling reason not to. The documentation was more open about no inverse relationships in 10.4, and we learned the hard way that was less than ideal. The modeling tool now issues warnings for this due to the frequency and severity of bugs from developers incorrectly and over eagerly using no inverse relationships. But oops, Core Data can't model cross-store relationships, so you use a fetched property, which is one-way. You could use a fetched property, or handle this in code by storing a URI for the destination object in a different store, and fetching the matching objectID either lazily in in -awakeFromFetch. We've generally recommended using a custom accessor method for this instead of fetched properties. Is there any particular reason for that recommendation? The documentation explicitly recommends fetched properties for cross-store relationships (one instance of several is in the Core Data Programming Guide, Relationships and Fetched Properties chapter, Fetched Properties section, first paragraph, where it says In general, fetched properties are best suited to modeling cross-store relationships...) First, custom accessor methods and -awakeFromFetch offer a vast amount of flexibility, and can be easier to tune for performance. Fetched properties are a fine alternative. But I like to also reinforce the understanding that not all your custom behavior needs to be encapsulated in your Core Data schema. You have full Objective-C objects and very powerful runtime support. Use it liberally. (Let me take this opportunity to say that for all the warnings that Core Data is not and never has been a database, almost every concept I see in it makes me think O/R mapper for SQLite.) Core Data is an O/R mapping framework, among other things. But O/R frameworks are not SQL databases. Modeling your data in any O/R framework as if you were writing SQL directly is inefficient and mistaken. Saying that Core Data is a database is like saying your compiler is an assembler. Well, the compiler suite uses an assembler, sure, and they both output object code in the end, but that does not mean the best way to use your compiler is to write in assembly. Nonetheless, Core Data does manage the data stored on disk as well as the representation of that data in memory; I don't see a tremendous difference between that and what SQLite does, other than Core Data providing a much effective organization of and means of access to that data. Core Data implements a lot of functionality on top of SQLite. From an API perspective, that it uses SQLite at all is an implementation detail. In any event, O/R systems present an OO view of your data, and have their own idioms closer to OOP. They are providing an abstraction layer and perform transformations on both your queries and result sets. Relational databases can support that, but in every O/R system, the ideal way of using the system is somewhat different from how one would write SQL directly against the database. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Core Data fetch with to-many relationship
When I log the test fetch results: NSArray *testFetchResults = [managedObjectContext fetchObjectsForEntityName:@Owner withPredicate:[NSString stringWithFormat:@ANY books.name like 'myPrefix*']]; NSLog([[(Owner *)[testFetchResults objectAtIndex:0] books] valueForKey:@name]); I get the following sample output: -- {( foo, bar, myPrefix-v1, myPrefix-v2 )} -- yup. Your print statement, though, is not for the objects you fetched, but their related books. These were the four test Book objects I originally associated with one Owner, and then saved to the managed object context. I'm unclear why the predicate statement: @ANY books.name like 'myPrefix*' would return all books, whether their name starts with 'myPrefix' or not. The output I would expect is: -- {( myPrefix-v1, myPrefix-v2 )} -- You're not fetching books, you're fetching Owner. The predicate fetch request pair together to say: Fetch all the Owners that have ANY (one or more) object in their books relationship with a name like 'myPrefix*' The log statement shows the first Owner in the results array has 2 books matching your predicate. It also has a bunch of others, but that's irrelevant to whether or not it matches the predicate. You seem to be thinking of this predicate @ALL books.name like 'myPrefix*' for which we don't currently generate SQL. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: KVO can be unsafe in -init?
On 08/09/2009, at 12:36 AM, John Chang wrote: Hi all, Question: is it unsafe for some reason to be adding yourself as a KVO observer during -init? We have a singleton with an -init that looks something like this: - (id)init { if ((self = [super init])) { _foo = [[NSMutableDictionary alloc] init]; _bar = [[NSMutableDictionary alloc] init]; [[XYZManager sharedManager] addObserver:self forKeyPath:@allObjects options:NSKeyValueObservingOptionInitial context:NULL]; As a general rule, in ObjC, C++, and Java it's a very bad idea to expose partially constructed objects to third parties, especially via global registries like KVO. In addition to the obvious problems, multi-threading and error handling are especially pernicious. The multi-threading issue is pretty self explanatory. The moment your uninitialized object is registered in KVO, others can invoke methods on it before you and your subclasses are done setting initial state. The error handling issue involves what happens when an initializer needs to error out. In Cocoa, it should return nil, and if necessary, deallocate its previous self (to prevent +alloc from being unbalanced, and hence leak). But this now means that third parties can reach a deallocated object via these registrations. It will complicate how carefully you'll need to write your -dealloc method. If the KVO registration is the last thing that happens, then it'll work, but of course, subclassing will break your assumptions. This falls under the hack category, and is not a pattern you want to replicate widely. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: watch changes to any properties on an object
Well, @dynamic doesn't have anything to do with KVO. It's just storage and accessors for properties. Core Data knows when non- dynamic modeled properties change too. It sets a dirty flag, just as you would have to. Most of that happens in -willChangeValueForKey:. Unfortunately, overriding that method was deprecated in 10.5. KVO no longer guarantees overrides will be called. In terms of knowing what to save, Core Data only tracks that at an object level, and uses snapshot deltas to compute the changed property set at the end. Some people invert the observing relationship to work around this. You can add code in your setters to set a dirty flag, or within your setters manually call a notify method on another object. If you have fewer objects, you could just use NSNotifications. Almost certainly worth filing an enhancement request at bugreport.apple.com - Ben Ok, thats what i thought. But just for implementation ideas, how does CoreData know when one of it's @dynamic properties is changed? It must set some sort of flag somewhere in order to know what to write out when it needs to save. How does it handle that? thx AC On Sep 3, 2009, at 12:27 PM, Jens Alfke wrote: On Sep 3, 2009, at 8:24 AM, Alexander Cohen wrote: I have a base object that needs to know when any of it's properties or subclasses properties have changed and set a dirty flag on itself. Is there a way to do this? No, not in general. Key-value observing requires knowing the exact property name(s) in advance. You'll need to set the 'dirty' flag manually. ˜Jens ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Core Data dog-slow when using first time after boot
On Sep 3, 2009, at 4:49 AM, Ruotger Skupin wrote: Since it's not a many to many, you can perform the prefetching effectively by hand using a fetch request to preload the relevant destination rows with an IN query based on the data you initially fetched for the source entity. You shouldn't have to, but if you've run into a bug, that's how you could workaround it. You still haven't described the NSPredicate you were using with filteredArrayUsingPredicate. Being more forthcoming about the context and details of your problem will help us get you answers more quickly. This is the predicate I was using for the test of the original post, but since I use Smart Folders predicates can look a lot different (i.e.: complex): (additionalInfo.onTheFlyIsInternal == 0 AND additionalInfo.isSuppressed != 1) AND (account.uniqueID IN {D1AB3788-00DF-4475-A979-CE3EFC3987B5} OR FALSEPREDICATE) You'll want to prefetch additionalInfo and account. Hm. Problem here is: As mentioned I use the predicate for a smart group. So the predicate can vary wildly and can reference basically any entity in the model. So what is the best strategy here? Decompile the predicate (which is built by an NSPredicateEditor) and prefetch the keypaths it takes? Prefetch everything? Prefetching everything is usually undesirable. The easiest approximate solution is to just track the hot button relationships for each of your core entities, and look up which keypaths to prefetch by entity name. Of course, you can also do a lot less filtering in memory if you pass the predicates to the database with the fetch request. You only need to prefetch relationships you are going to use in memory. You don't need to prefetch anything that's simply used as part of the predicate in the fetch request itself. Walking through the predicate and gathering up the keypaths used is very tedious, but not especially difficult, if you find yourself wanting a complete solution. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: watch changes to any properties on an object
On Sep 3, 2009, at 12:14, Alexander Cohen wrote: Ah, ok, this is more like what i wanted to hear! :) I understand how @dynamic works, but how to I get to funnel all calls to @dynamic properties to the same call such as setValue:forKey: or something like that where i can parse the key and update my internal data and set the flags i need to set. No, you're barking up the wrong tree. There's no how @dynamic works. @dynamic is a compiler directive telling it that getter/setter method implementations exist, but just not in the current compilation unit. There no standard general mechanism for supplying the implementation. In the case of Core Data specifically, the built-in implementations (call them dynamic if you want, but that's the same as their being compiled as @dynamic) are simply efficient versions of what you would otherwise have to hand-code. We don't know if they're funneled through one funnel, several funnels, or a different function for every property -- that's an implementation detail. (IAC, Core Data doesn't mark objects as changed in *those* dynamic methods, but (presumably -- another implementation detail) in the primitiveKey dynamic methods.) I don't how you're ever going to be able to have a class detect invocations of its subclasses' properties, unless you have the class muck around in the runtime, replacing methods on the fly. A better solution, IMO, is to realize that you're considering a design requirement for your data model, and to design the solution right into the model. For example, if this is a self-contained class hierarchy that you're implementing, you could make it a requirement of subclasses that they invoke something (a superclass method) or inform something (a controller of some kind) when they modify data values. Good advice. Also, instead of worrying about how Core Data does this, you could just leverage Core Data's change tracking, whether via inheritance or composition, and respond to the NSManagedObjectContextObjectsDidChangeNotification. You don't have to save to one of Core Data's persistence mechanisms just to create a bunch of managed objects to hold some properties. - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Correct way to have nil undo manager in Core Data document?
Well, I've got a background worker process which opens an NSPersistenDocument. Since it has no undo capability, I want to set it to nil as recommended. (I have other reasons for not wanting an undo manager scurrying around.) But the document doesn't like having nil undo manager. When I ask for it later, it creates one for itself, and sets its managed object context to have the same one. You need to use the NSDocument API: /* The document's undo manager. The default implementation of - setUndoManager:, in addition to recording the undo manager, registers the document as an observer of various NSUndoManager notifications so that -updateChangeCount: is invoked as undoable changes are made to the document. The default implementation of -undoManager creates an undo manager if the document does not already have one and - hasUndoManager would return YES. */ - (void)setUndoManager:(NSUndoManager *)undoManager; - (NSUndoManager *)undoManager; /* Whether or not the document has an undo manager. The default implementation of -setHasUndoManager: releases the document's current undo manager if it has one before the invocation but is not to have one afterward. */ - (void)setHasUndoManager:(BOOL)hasUndoManager; - (BOOL)hasUndoManager; - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Core Data Predicate Builder - comparing dates
I have a Core Data entity that has a dateCreated and a dateModified - both NSDates in the object files. I'd like to construct a predicate that will retrieve all records where a record's dateModified is greater than that record's dateCreated. Its deceptively easy to setup something that looks like it should work using 'Control-click' and 'Key' in the predicate builder in XCode. But when I run queries it doesn't appear to yield the right results. My thinking is that the comparison operators don't properly evaluate dates (am I wrong?) Comparison operators work just fine on dates, although == and != are fragile since NSDates are double time stamps, and floating point numbers have odd == behavior. What does the predicate look like ? How is it not working ? What does SQL logging show ? So dropping back to code - how would I write this predicate in code? [NSPredicate predicateWithFormat:@dateModified dateCreated] - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
re: Curious about $SUBST_VARS within a SUBQUERY of a fetch request template
I just created a fetch request template in my MOM that looked like: SUBQUERY(platformInfos, $each, $each.platformName == $PLATFORM_NAME)@count == 0 I then looked up the template in the code and attempted to use it in the usual manner with: fetchRequestFromTemplateWithName:substitutionVariables: should work Core Data complained that it could not resolve the SQL for $each.platformName == $PLATFORM_NAME, because of the RHS. I would have thought that this variable would be replaced as normal, even though it is inside a SUBQUERY. Does anybody know if this is officially disallowed, or a bug, or just developer-error somehow? What your code look like for the substitution variables dictionary ? - Ben ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com