Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Observing changes to an object w/ KVO, context pointer doesn't seem to be working with a void*, but works with an NSString*

So I have a MainController,
inside of the header I have this void* declared:

static void *kStrokeColorWellChangedContext = &kStrokeColorWellChangedContext;

Inside of the implementation I am handling observation:

-(void)observeValueForKeyPath:(NSString *)keyPath
                     ofObject:(id)object
                       change:(NSDictionary *)change
                      context:(void*)context
{
   if( [keyPath isEqualToString:@"color"] )
   {
      if( context == kStrokeColorWellChangedContext )
      {
         [self setValue:[change objectForKey:@"new"] forKey:@"strokeColor"];
      }
   }

Then I have a ViewController which has this line, which adds the MainController as an observer to a view

[self.strokeColorWell addObserver:self.toolController forKeyPath:@"color" options:NSKeyValueObservingOptionNew context:kStrokeColorWellChangedContext];

So when the view's color changes, it notifies the MainController, and enters the -observeValueForKey: routine, w/ the key @"color"

But it fails to enter into the inner if block, where I am checking context.

If I were to swap out my void* with an NSString* instead, it works as I would expect.

How could it be possible that the void* appears to have two different addresses stored into it?

EDIT: Maybe the values of the void* could help

the result of po kStrokeColorWellChangedContext:

0x00000001005553e8

the result of po context in -observeValueForKey::

0x0000000100555ea0

objc[17775]: Method cache corrupted. This may be a message to an invalid object, or a memory error somewhere else.
objc[17775]: receiver 0x100555ea0, SEL 0x7fff899c8246, isa 0x100555ea0, cache > 0x100555eb0, buckets 0x100566160, mask 0x0, occupied 0x0
objc[17775]: receiver 0 bytes, buckets 0 bytes
objc[17775]: selector 'respondsToSelector:'

The exceptions being thrown there may be a hint, but I really don't understand any of it

EDIT2: The output of p instead of po

(lldb) p context
(void *) $5 = 0x0000000100555ea0
(lldb) p kStrokeColorWellChangedContext
(void *) $6 = 0x00000001005553e8

Please note that I am pretty-very confident that I am debugging the correct notification.
Two reasons why; the keyPath is unique and I see that value is correct. Also if I use an NSString* as the context it works

like image 820
A O Avatar asked Oct 05 '15 19:10

A O


2 Answers

The problem is that you're defining kStrokeColorWellChangedContext as a static variable in a header. Each translation unit (source file, basically) that imports that header will create a separate variable by that name, because it's declared static and that's what that means for variables at file scope.

So, each variable will have a separate address and, since the variable's value is its own address, each will have a different value.

You could declare the variable in the header and define it in an implementation such that there's only one such variable. The declaration in the header would look like:

extern void * const kStrokeColorWellChangedContext;

The definition in one of the implementation files would look like:

void * const kStrokeColorWellChangedContext = (void*)&kStrokeColorWellChangedContext;

Note that I've made the variable a const pointer to void. That disallows anything from assigning a new value to it, since it's supposed to be a constant.

like image 75
Ken Thomases Avatar answered Oct 03 '22 00:10

Ken Thomases


This works for me - kStrokeColorWellChangedContext "has" two addresses: its position in memory and its value and they're both the same! So kStrokeColorWellChangedContext and & kStrokeColorWellChangedContext should be interchangeable throughout your program.

Have you tried stepping through your KVO callback and looking at/logging the values of context and kStrokeColorWellChangedContext? And keyPath?

like image 27
Rhythmic Fistman Avatar answered Oct 02 '22 23:10

Rhythmic Fistman