This is a resolution of my message posted 2010 Jan 02, subject:
   Bindings/Core Data: Undesired Discovery of a Mythical "Deep Observer"

I'm posting this because, after rewriting the whole thing and giving it some 
more thought, I believe I have an explanation, which archive-searchers might 
find interesting.  Summary: If an NSArrayController with 'contentArray' bound 
to a data model receives from the data model a new array which is equal to but 
a different pointer value than the array it currently has, it will push the new 
array back through the setter of the data model.

I'm working on a Core Data project.  Some to-many properties are displayed in 
tables, and these objects in turn have their own attributes which are displayed 
in the columns, bound to array controllers' -arrangedObjects.xxx, etc.  Pretty 
standard stuff...

    DEPARTMENT's EMPLOYEES
 Name        Rank       Salary
-------      ----       ------
Fat Cat       3          200

except that I have subclassed NSManagedObject and added 'index' attributes so 
that I can treat the sets as arrays, providing methods, for example 
-(NSArray*)employeesOrdered, -(Employee*)newEmployeeAtIndex:, etc.  All this 
works OK, except in a few of my columns I notice that, taking the above 
example, if user edits the table to change, say, the 'rank' of Fat Cat, after 
sending setRank:, the *Department* gets a -setEmployeesOrdered: message, with 
an NSArray argument which is a different pointer value but otherwise equal to 
the existing employeesOrdered array; it contains the same single object with 
the same pointer value.

This does no harm to the data model of course; the only reason I noticed it is 
because in some cases it overwrites and thus screws up my undo action names 
which are driven by custom setters.

In real life, -setEmployeesOrdered: is actually -setExternalizersOrdered and is 
#9 in the call stack below.  To fix the problem, in this setter I simply first 
check for array equality and return if no change.  Looking at the calls lower 
down, it appears to be fulfilling the binding on the array controller which 
causes this unnecessary message.  Of course, I do have an array controller with 
contentArray bound to 'externalizersOrdered'.  Also, it makes sense that there 
would be different pointer values of externalizersOrdered floating around since 
the getter computes it from the underlying set.  See code at the end.

I've decided that, apparently what's happening is that when when the array 
controller notices that a bound (by a table column) to attribute is changed, it 
asks the data model for -externalizersOrdered, sees that it gets a different 
array, but does not bother to check and see that the new array, although a 
different pointer value, is equal to the old array.  Probably it only keeps a 
reference.  And then due to some quirk in bindings it says, "Oh, I better push 
this new array to the data model's setter" (even though it just got it from the 
data model).

If anyone has read this far and has a better explanation, let us know.

#0      0x00122961 in -[Bkmslf 
setUndoActionNameForAction:object:objectKey:updatedKey:count:] at Bkmslf.m:2928
#1      0x0001fd71 in -[Bookshig 
mikeAshObserveValueForKeyPath:ofObject:change:userInfo:] at Bookshig.m:1789
#2      0x000ff452 in -[MAKVObservation 
observeValueForKeyPath:ofObject:change:context:] at MAKVONotificationCenter.m:98
#3      0x002d6208 in NSKeyValueNotifyObserver
#4      0x002d5ca7 in NSKeyValueDidChange
#5      0x002ba6d0 in -[NSObject(NSKeyValueObserverNotification) 
didChangeValueForKey:]
#6      0x01baa375 in -[NSManagedObject didChangeValueForKey:]
#7      0x0006e3fb in -[Ixternalizer setIndex:] at Ixternalizer.m:351
#8      0x000b6d93 in -[SSYManagedObject setWithIndexesArray:forSetKey:] at 
SSYManagedObject.m:231
#9      0x0001d7f5 in -[Bookshig setExternalizersOrdered:] at Bookshig.m:1233
#10     0x002dec99 in _NSSetObjectValueAndNotify
#11     0x01ba726a in -[NSManagedObject setValue:forKey:]
#12     0x002eead3 in -[NSObject(NSKeyValueCoding) setValue:forKeyPath:]
#13     0x002eeaaf in -[NSObject(NSKeyValueCoding) setValue:forKeyPath:]
#14     0x002eeaaf in -[NSObject(NSKeyValueCoding) setValue:forKeyPath:]
#15     0x0078f0d6 in -[NSBinder 
_setValue:forKeyPath:ofObject:mode:validateImmediately:raisesForNotApplicableKeys:error:]
#16     0x0078eeb0 in -[NSBinder setValue:forBinding:error:]
#17     0x00b14745 in -[NSObjectDetailBinder 
setMasterObjectRelationship:refreshDetailContent:]
#18     0x00b1461d in -[NSObjectDetailBinder noteContentValueHasChanged]
#19     0x008f27ac in -[NSArrayController _setMultipleValue:forKeyPath:atIndex:]
#20     0x0078f211 in -[NSBinder 
_setValue:forKeyPath:ofObject:mode:validateImmediately:raisesForNotApplicableKeys:error:]
#21     0x00903832 in -[NSBinder setValue:forBinding:atIndex:error:]
#22     0x0078ed3d in -[_NSValueBinderPlugin 
applyObjectValue:forBinding:operation:needToRunAlert:error:]
#23     0x00ca9a4d in -[NSValueBinder 
_applyObjectValue:forBinding:canRecoverFromErrors:handleErrors:typeOfAlert:discardEditingCallback:otherCallback:callbackContextInfo:didRunAlert:]
#24     0x00ca98a3 in -[NSValueBinder 
applyDisplayedValueHandleErrors:typeOfAlert:canRecoverFromErrors:discardEditingCallback:otherCallback:callbackContextInfo:didRunAlert:]
#25     0x0078e647 in -[NSValueBinder performAction:]
#26     0x0078e3f6 in -[_NSBindingAdaptor 
_objectDidTriggerAction:bindingAdaptor:]
#27     0x0078e333 in -[_NSBindingAdaptor objectDidTriggerAction:]
#28     0x007926d9 in -[NSControl sendAction:to:]
#29     0x0078e1ba in -[NSCell _sendActionFrom:]
#30     0x006b2f86 in -[NSApplication sendAction:to:from:]
#31     0x006b2e39 in -[NSMenuItem _corePerformAction]
#32     0x006b2b2a in -[NSCarbonMenuImpl 
performActionWithHighlightingForItemAtIndex:]
#33     0x006b2a16 in -[NSMenu performActionForItemAtIndex:]
#34     0x006b29c9 in -[NSMenu _internalPerformActionForItemAtIndex:]
#35     0x006b292f in -[NSMenuItem _internalPerformActionThroughMenuIfPossible]
#36     0x006b2873 in -[NSCarbonMenuImpl 
_carbonCommandProcessEvent:handlerCallRef:]
#37     0x006a6f79 in NSSLMMenuEventHandler
#38     0x03396e29 in DispatchEventToHandlers
#39     0x033960f0 in SendEventToEventTargetInternal
#40     0x033b8981 in SendEventToEventTarget
#41     0x033e4e3b in SendHICommandEvent
#42     0x03409b20 in SendMenuCommandWithContextAndModifiers
#43     0x03409ad7 in SendMenuItemSelectedEvent
#44     0x034099d3 in FinishMenuSelection
#45     0x0358ab82 in PopUpMenuSelectCore
#46     0x0358aed0 in _HandlePopUpMenuSelection7
#47     0x0093c61e in _NSSLMPopUpCarbonMenu3
#48     0x00b30a32 in -[NSPopUpButtonCell 
trackMouse:inRect:ofView:untilMouseUp:]
#49     0x007efa02 in -[NSTableView 
_tryCellBasedMouseDown:atRow:column:withView:]
#50     0x007ec79f in -[NSTableView mouseDown:]
#51     0x00789f10 in -[NSWindow sendEvent:]
#52     0x000b2684 in -[SSYDocChildWindow sendEvent:] at SSYDocChildWindow.m:18
#53     0x006a2b2f in -[NSApplication sendEvent:]
#54     0x006364ff in -[NSApplication run]
#55     0x0062e535 in NSApplicationMain
#56     0x00001e67 in main at MainApp-Main.m:19


- (NSArray*)externalizersOrdered {
    return [[self externalizers] arraySortedByKeyPath:constKeyIndex] ;
}

- (void)setExternalizersOrdered:(NSArray*)externalizersOrdered {
    // Patch to ignore re-setting to an equal array:
    if ([externalizersOrdered isEqualToArray:[self externalizersOrdered]]) {
        return ;
    }

    [self setWithIndexesArray:externalizersOrdered
                    forSetKey:constKeyExternalizers] ;
}

+ (NSSet *)keyPathsForValuesAffectingExternalizersOrdered {
    return [NSSet setWithObject:constKeyExternalizers] ;    
}

_______________________________________________

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

Reply via email to