This is from my window controller class, which has a (optional) reference to my 
document subclass’s model data. Through Interface Builder, that data is 
connected to a NSObjectController, and a property of that data is connected to 
a NSArrayController. The window controller has outlets to both data 
controllers. The model data is Core Data-based, BTW.

I have to watch the data as part of my scheme to create a character map to use 
for NSTextFinder. Instead of KVO-ing the model data, I do the two data 
controllers instead since those are the “truth” I need to track (in case they 
cache any editing changes).

>         headerController.addObserver(self, forKeyPath: 
> MessageWindowController.headerKeyPath, options: .initial, context: 
> &MessageWindowController.headerObservingContext)
>         messageController.addObserver(self, forKeyPath: 
> MessageWindowController.bodyKeyPath, options: .initial, context: 
> &MessageWindowController.bodyObservingContext)

The header observation points to the array controller’s “arrangedObjects”. The 
message observation points to the object controller’s “selection”, then a 
specific property of my model (of type “String?”). I try to test an incomplete 
version of the KVO function:

>     override func observeValue(forKeyPath keyPath: String?, of object: Any?, 
> change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
>         guard let keyPath2 = keyPath, let object2 = object, let change2 = 
> change, let context2 = context else {
>             return super.observeValue(forKeyPath: keyPath, of: object, 
> change: change, context: context)
>         }
> 
>         let changeKind = NSKeyValueChange(rawValue: change2[.kindKey] as! 
> UInt)!
>         switch context2 {
>         case &MessageWindowController.headerObservingContext:
>             precondition(keyPath2 == MessageWindowController.headerKeyPath)
>             precondition(object2 as AnyObject === headerController)
>             break
>         case &MessageWindowController.bodyObservingContext:
>             precondition(keyPath2 == MessageWindowController.bodyKeyPath)
>             precondition(object2 as AnyObject === messageController)
>             assert(changeKind == .setting)
> 
>             let newString = (messageController.selection as! 
> NSObject).value(forKey: #keyPath(RawMessage.body)) as! String? 
> //messageController.value(forKeyPath: keyPath2) as! String?
>             let bodyTextRangeIndex = textRanges.index(before: 
> textRanges.endIndex)
>             let oldBodyTextRange = textRanges[bodyTextRangeIndex]
>             let newLength = (newString as NSString?)?.length ?? 0
>             if oldBodyTextRange.length != newLength {
>                 textRanges[bodyTextRangeIndex] = 
> NSMakeRange(oldBodyTextRange.location, newLength)
>             }
>             break
>         default:
>             super.observeValue(forKeyPath: keyPath2, of: object2, change: 
> change2, context: context2)
>         }
>     }

And there’s a crash at the “newString” definition. (The current and 
commented-out versions get the same error. The current version was copied from 
my menu-item validation function, where it actually works.):

> Could not cast value of type '_NSStateMarker' (0x7fffa3003cf8) to 'NSString' 
> (0x7fffa397df38).
> 2017-03-27 16:36:33.978709 XNW[39160:4830169] Could not cast value of type 
> '_NSStateMarker' (0x7fffa3003cf8) to 'NSString' (0x7fffa397df38).

What is this “NSStateMarker” class? And why does it prevent me from accessing 
the data?

Do NSObjectController and/or NSArrayController do any caching of its editing 
data? If not, and we can’t directly solve this problem, I could read the data 
directly from the document’s model objects.

Pre-Send Update:

I took out the “.initial” from the observing setup call, and it works! Why is 
that? Now I have to hope that my concept of the initial text-range array is 
accurate, since I can’t confirm it with an initial pass (for now).

Note that when the window controller goes through “windowDidLoad”, the 
reference to the model data is NIL. (The object and array controllers are still 
bound to it by then, though.) It doesn’t get set to an actual data tree until 
the document’s “makeWindowControllers” call.

Pre-Send Update 2:

I completed a first try for the other property:

>         let changeKind = NSKeyValueChange(rawValue: change2[.kindKey] as! 
> UInt)!
>         let bodyTextRangeIndex = textRanges.index(before: textRanges.endIndex)
>         switch context2 {
>         case &MessageWindowController.headerObservingContext:
>             precondition(keyPath2 == MessageWindowController.headerKeyPath)
>             precondition(object2 as AnyObject === headerController)
> 
>             let newArray = headerController.mutableArrayValue(forKeyPath: 
> keyPath2) as NSArray
>             switch changeKind {
>             case .insertion, .removal, .replacement:
>                 fallthrough
>             case .setting:
>                 var newFieldRanges = RangeArray()
>                 for newField in newArray {
>                     let field = newField as! RawHeaderField
>                     newFieldRanges.append(contentsOf: [field.name, 
> field.body].map { NSMakeRange(0, ($0 as NSString).length) })
>                 }
>                 textRanges.replaceSubrange(textRanges.startIndex ..< 
> bodyTextRangeIndex, with: newFieldRanges)
>             }

This one still works when “.initial” is active.

— 
Daryle Walker
Mac, Internet, and Video Game Junkie
darylew AT mac DOT com 

_______________________________________________

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to