Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Sijmen

macrumors 6502a
Original poster
Sep 7, 2005
709
1
I was writing an MVC app today. I noticed that to expose a binding, you have to write a lot of code for each one, all which are much alike. Like you have to observe value changes, store the observable and the key path, etc.

Am I overlooking something here or is exposing bindings a pain? Is there a reason why there's no standard API to keep two properties (or KVC/KVO compliant attributes) in sync?


To clarify, just in case, here's one of my classes to see what I'm talking about.

Code:
//
//  DimpBaseView.h
//  Dimp
//
//  Created by Sijmen Mulder on 20-03-08.
//  Copyright 2008 __MyCompanyName__. All rights reserved.
//

#import <Cocoa/Cocoa.h>

@class Bone;

@interface DimpBaseView : NSView
{
	id rootBoneBindingObject;
	id currentTimeBindingObject;

	NSString *rootBoneBindingPath;
	NSString *currentTimeBindingPath;
	
	Bone *rootBone;
	float currentTime;
}

@property (readwrite, retain) Bone *rootBone;
@property (readwrite) float currentTime;

@end


//
//  DimpBaseView.m
//  Dimp
//
//  Created by Sijmen Mulder on 20-03-08.
//  Copyright 2008 __MyCompanyName__. All rights reserved.
//

#import "DimpBaseView.h"

static void *RootBoneBindingContext = (void *)@"RootBoneBindingContext";
static void *CurrentTimeBindingContext = (void *)@"CurrentTimeBindingContext";

@implementation DimpBaseView

- (void)bind:(NSString *)binding toObject:(id)observable withKeyPath:(NSString *)keyPath options:(NSDictionary *)options
{
	if ([binding isEqualToString:@"rootBone"])
	{
		if (rootBoneBindingObject)
			[self unbind:binding];
			
		rootBoneBindingObject = observable;
		rootBoneBindingPath = keyPath;
		
		[rootBoneBindingObject addObserver:self forKeyPath:keyPath options:0 context:RootBoneBindingContext];
	}
	else if ([binding isEqualToString:@"currentTime"])
	{
		if (currentTimeBindingObject)
			[self unbind:binding];
			
		currentTimeBindingObject = observable;
		currentTimeBindingPath = keyPath;
		
		[currentTimeBindingObject addObserver:self forKeyPath:keyPath options:0 context:CurrentTimeBindingContext];
	}
	else
	{
		[super bind:binding toObject:observable withKeyPath:keyPath options:options];
	}
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
	if (context == RootBoneBindingContext)
	{
		[self setValue:[rootBoneBindingObject valueForKey:rootBoneBindingPath] forKey:@"rootBone"];
	}
	else if (context == CurrentTimeBindingContext)
	{
		[self setValue:[currentTimeBindingObject valueForKey:currentTimeBindingPath] forKey:@"currentTime"];
	}
	else
	{
		[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
	}
}

- (void)unbind:(NSString *)binding
{
	if ([binding isEqualToString:@"rootBone"])
	{
		[rootBoneBindingObject removeObserver:self forKeyPath:rootBoneBindingPath];
		
		rootBoneBindingObject = nil;
		rootBoneBindingPath = nil;
	}
	else if ([binding isEqualToString:@"currentTime"])
	{
		[currentTimeBindingObject removeObserver:self forKeyPath:currentTimeBindingPath];
	
		currentTimeBindingObject = nil;
		currentTimeBindingPath = nil;
	}
	else
	{
		[super unbind:binding];
	}
}

- (Bone *)rootBone
{
	return rootBone;
}

- (void)setRootBone:(Bone *)value
{
	if (rootBone == value)
		return;
	
	rootBone = value;
	
	if (rootBoneBindingObject)
		[rootBoneBindingObject setValue:value forKeyPath:rootBoneBindingPath];
		
	[self didChangeValueForKey:@"rootBone"];
	[self setNeedsDisplay:YES];
}

- (float)currentTime
{
	return currentTime;
}

- (void)setCurrentTime:(float)value
{
	if (currentTime == value)
		return;
		
	currentTime = value;
	
	if (currentTimeBindingObject)
		[currentTimeBindingObject setValue:[NSNumber numberWithFloat:value] forKeyPath:currentTimeBindingPath];
		
	[self didChangeValueForKey:@"rootBone"];
	[self setNeedsDisplay:YES];
}

@end
 

kainjow

Moderator emeritus
Jun 15, 2000
7,958
7
I've never done custom bindings before, but it looks about right. Not too much extra code, but I'm sure you could simplify a lot of that down, since you seem to be doing a lot of similar code in each if statement (but don't forget readability > cleverness ;)). But the advantage is you do your glue code only once, and use bindings elsewhere.


By the way, is your last name really Mulder? That's awesome :cool:
 

Sijmen

macrumors 6502a
Original poster
Sep 7, 2005
709
1
I've never done custom bindings before, but it looks about right. Not too much extra code, but I'm sure you could simplify a lot of that down, since you seem to be doing a lot of similar code in each if statement (but don't forget readability > cleverness ;)). But the advantage is you do your glue code only once, and use bindings elsewhere.


By the way, is your last name really Mulder? That's awesome :cool:

Thanks foor looking :) my last name is Mulder indeed.

I feel this way of binding is a bit tedious. It requires 3 variables and some 20 lines of code per binding. Ough. But just wanted to make sure that this is how it's supposed to be, and I'm not missing on something obvious.
 

Eraserhead

macrumors G4
Nov 3, 2005
10,434
12,250
UK
I don't usually bother in my coding for D&D Manager, and I just use set/get methods when the built in bindings aren't enough.
 

springframework

macrumors member
Mar 31, 2008
59
0
hey, can someone post the most simple project that used bindings? preferably a project that only contains one simple binding.

just something simple like a variable that is bound to a text field. so the text field is auto filled with the variable value, and when the text field changes the variable gets changed to the new value of the text field.

After reading the documentation on bindings, they seem poor to me. i would really like see if anyone finds them useful to use. and i would like to know if anyone actually uses them throughout their application.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.