4

I have a helper class that distributes a shared instance of UIManagedDocument. The idea is that the user requests the UIManagedDocument shared instance for a particular file on disk. In this case, it's a core data store. If the user requests the core data store located at a different path I want to distribute an instance of UIManagedDocument for that file.

My question is: Is it ok to create a new instance of a UIManagedDocument and assign it to the static variable when the file changes? For example:

+ (UIManagedDocument *)sharedManagedDocumentForFile:(NSString *)fileName
{
    static UIManagedDocument *sharedDocument = nil;

    NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
    url = [url URLByAppendingPathComponent:fileName];
    // url is "<Documents Directory>/<vacationName>"

    // Create the shared instance lazily upon the first request.
    if (sharedDocument == nil) {
        sharedDocument = [[UIManagedDocument alloc] initWithFileURL:url];
    }

    if (sharedDocument.fileURL != url) {
        UIManagedDocument *newDocument = [[UIManagedDocument alloc] initWithFileURL:url];
        sharedDocument = newDocument;
    }

    return sharedDocument;
}

Basically what I'm trying to do is distribute only one instance of a UIManagedDocument so in the event there are multiple writers to the core data store I don't have to constantly keep the changes in sync. However, since there are multiple core data stores on disk I can't just distribute the same static variable every time.

Any ideas? I'm absolutely stuck on even how to approach this design problem... Any help is appreciated.

Thanks - Jake

Joey J
  • 1,355
  • 10
  • 26
  • 5
    I believe this comes with a mandatory sentence. – PengOne Jan 17 '12 at 23:16
  • @Jave V I think he was joking...lol – bschultz Jan 17 '12 at 23:19
  • What exactly do you mean by 'ok' and 'legal'? – occulus Jan 17 '12 at 23:30
  • By 'ok' I mean would I run into some bugs or issues by changing the memory address pointed to by the static variable. – Joey J Jan 17 '12 at 23:35
  • 2
    This does not look like 'shared' document, but rather like 'currentDocument'. If so, deal with it appropriately: make a corresponding property somewhere higher in the hierarchy (application delegate for example), use the property respectively and get rid of the class method and the static variable. – Davyd Geyl Jan 17 '12 at 23:55

2 Answers2

6

Ok, if you're trying to do what I think you're trying to do: no, this won't work.

I'm assuming you want to persist a sharedDocument for each unique file on disk requested, independently of any other sharedDocuments in existence. But your code won't do that, because every time a fileName is passed in which is different from the last passed in fileName, the reference to the old UIManagedDocument is lost.

Imagine the following (contrived) scenario:

UIManagedDocument *docA = [self sharedManagedDocumentForFile:@"fileA.txt"];
UIManagedDocument *docB = [self sharedManagedDocumentForFile:@"fileB.txt"];
UIManagedDocument *docA2 = [self sharedManagedDocumentForFile:@"fileA.txt"];

You're expecting docA and docA2 to be the same UIManageDocument but that won't happen, because the middle line caused your static variable to forget the original managed document for file1.txt1.

I'd abandon the use of the static variable. There's many other ways you could do this. A simple way would be to use an NSMutableDictionary to map from filenames to UIManagedDocument instances. Something like this:

+ (UIManagedDocument *)sharedManagedDocumentForFile:(NSString *)fileName
{
    //assuming we have an instance variable:  NSMutableDictionary *docDictionary
    UIManagedDocument *doc = [docDictionary objectForKey:fileName];

    if (!doc) {
        NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
        url = [url URLByAppendingPathComponent:fileName];
        // url is "<Documents Directory>/<vacationName>"

        doc = [[UIManagedDocument alloc] initWithFileURL:url];

        [docDictionary setObject:doc forKey:fileName];
    }
    return doc;
}

Update

Because sharedManagedDocumentForFile: is a class method, you can't store docDictionary as an instance variable for your class, as you note. Instead, you can declare it in your .m file before your class implementation like so:

static NSMutableDictionary *docDictionary = nil;

@implementation MyClass ...

This in effect gives a single docDictionary instance that exists outside of any instances of your class. Instances of your classes can still access it though.

The static keyword ensures that this docDictionary variable can't be accessed outside the current compilation unit (i.e. source file). For more info on static and its many different meanings, see questions such as Difference between static in C and static in C++??

Community
  • 1
  • 1
occulus
  • 16,959
  • 6
  • 53
  • 76
  • Occulus - thank you for the response. What you suggested is a better solution for the reasons you explained with your example. There is one thing I don't understand. How can we access the instance variable docDictionary if the `sharedManagedDocumentForFile` is a class method? – Joey J Jan 18 '12 at 01:59
  • Godo question. Updating my answer. – occulus Jan 18 '12 at 12:22
  • Thank you! .. I've implemented this and it seems to be working perfectly. – Joey J Jan 18 '12 at 19:13
  • Please fix addObject to setObject – Shmidt Apr 26 '12 at 11:38
0

I figured it out. Use setObject, not addObject.

Michele
  • 3,617
  • 12
  • 47
  • 81