4

In the current headers, it's declared as:

@property (nonatomic, readonly, strong) NSMutableDictionary *userInfo NS_AVAILABLE(10_7,  5_0);

The documentation simply says:

Returns the receiver’s user info.

  1. What is the purpose of the context's userInfo? Other Core Data objects have userInfo dictionaries that can be set in the managed object model. That does not seem to be the case here. Is it just meant as a temporary stash that’s more convenient than using associated objects?

  2. Is it ever saved to the persistent store or via NSCoding?

  3. Does it use the same threading model as the context’s managed objects?

Community
  • 1
  • 1
Michael Tsai
  • 1,945
  • 17
  • 41

2 Answers2

4

What is the purpose of the context's userInfo?

I took a look at this several ways, and unfortunately did not find anything definitive.

  • Set a symbolic breakpoint on -[NSManagedObjectContext userInfo] and ran a set of Core Data regression tests that exercise a large part of the public API.
  • Looked through a Core Data binary framework using several reverse engineering tools.

Using a symbolic breakpoint did not yield much, though it's possible that the version of Xcode I used just blew past it. It doesn't seem that anything is accessing the public accessor, but given the internals of Core Data that isn't terribly surprising.

Disassembling that method yielded some hints:

void * -[NSManagedObjectContext userInfo](void * self, void * sel) {
    rsi = sel;
    rbx = self;
    if (*(int32_t *)__PF_Threading_Debugging_level != 0x0) {
            __PFAssertSafeMultiThreadedAccess_impl(rbx, rsi);
    }
    rax = *_OBJC_IVAR_$_NSManagedObjectContext._additionalPrivateIvars;
    rax = *(rbx + rax);
    rax = *(rax + 0x30);
    return rax;
}

The method does check wether concurrency debugging is active, and wether this method is being used correctly within the concurrency rules (this answers question 3). The method is directly accessing the private instance variable _additionalPrivateIvars, which other parts of Core Data read and write from. Some of the change tracking/progation and optimistic locking methods use this instance variable. -lockObjectStore, for example, writes to _additionalPrivateIvars on NSManagedObjectContext.

It's a bit strange that it's an NSMutableDictionary that's read-only. You can't set a new NSMutableDictionary, but you could happily set keys and values on it. I was able to do this pretty easily:

(lldb) po [[[result managedObjectContext] userInfo] setValue:@"foo" forKey:@"bar"]
0x0000000000000020

(lldb) po [[result managedObjectContext] userInfo]
{
    bar = foo;
}

Without more detailed guidance from Apple, I would not want to do this in production code as it may be dangerous to write to this.

Is it ever saved to the persistent store or via NSCoding?

It doesn't appear to be, and doing so would not make much sense. Persistent stores don't know much of anything about the contexts that access them through a coordinator, and if the userInfo dictionary were important to the store it should be covered in the Atomic Store Programming Guide and Incremental Store Programming Guide.

Does it use the same threading model as the context’s managed objects?

It definitely does, and if concurrency debugging is active misuse of -userInfo will log an assert.

Community
  • 1
  • 1
quellish
  • 21,123
  • 4
  • 76
  • 83
1

The documentation on this specific API is remarkably sparse on this (the total text is: The receiver’s user info.). However, the userInfo dictionary is very standard in Apple’s frameworks. It’s available to the user to add custom information without the need to create a subclass (see, for example, the documentation on NSNotification’s userInfo).

The fact that NSManagedObjectContext’s userInfo is read only is not a problem. Instead of assigning the property to a dictionary as you might expect to do:

// useful while debugging multiple Core Data threads
*moc.userInfo = @{@"name":@"main managed object context"};

just access the dictionary directly (it is, after all, mutable):

*moc.userInfo[@"name"] = @"main managed object context"
Demitri
  • 13,134
  • 4
  • 40
  • 41