- Issue #75, Johannes Fortmann's C++ constructor and bindings patch:

With this patch, the .cxx_construct and .cxx_destruct methods will be called on NSAllocateObject/NSDeallocateObject. Note that you'll have to run gcc with -fobjc-call-cxx-cdtors to have these methods created.
Additionally, there are some KVO and bindings fixes:
- observing a change inside a hierarchy would give the wrong object as being changed
- array operator @sum implemented
- NSArrayController selectedObjects now depends on selection
- some additional binding parameters implemented
This commit is contained in:
Christopher Lloyd 2008-03-12 19:25:28 +00:00
parent e331c69257
commit d204ace317
13 changed files with 130 additions and 21 deletions

View File

@ -43,7 +43,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
{
[self setKeys:[NSArray arrayWithObjects:@"contentArray", @"selectionIndexes", nil]
triggerChangeNotificationsForDependentKey:@"selection"];
[self setKeys:[NSArray arrayWithObjects:@"contentArray", @"selectionIndexes", nil]
[self setKeys:[NSArray arrayWithObjects:@"contentArray", @"selectionIndexes", @"selection", nil]
triggerChangeNotificationsForDependentKey:@"selectedObjects"];
[self setKeys:[NSArray arrayWithObjects:@"selectionIndexes", nil]
triggerChangeNotificationsForDependentKey:@"canRemove"];
@ -249,11 +249,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
#pragma mark -
#pragma mark Moving selection
-(BOOL)canInsert {
//NSUnimplementedMethod();
return NO;
}
-(BOOL)canSelectPrevious
{
id idxs=[[[self selectionIndexes] mutableCopy] autorelease];
@ -389,6 +384,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
{
id _keyPath;
id _observer;
id _object;
}
-(id)initWithKeyPath:(id)keyPath observer:(id)observer;
-(id)observer;
@ -436,15 +432,16 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
// count never changes (immutable array)
if([keyPath isEqualToString:@"@count"])
return;
_NSObservationProxy *proxy=[[_NSObservationProxy alloc] initWithKeyPath:keyPath
observer:observer];
observer:observer
object:self];
[_observationProxies addObject:proxy];
[proxy release];
NSString* firstPart, *rest;
[keyPath _KVC_partBeforeDot:&firstPart afterDot:&rest];
[_array addObserver:proxy
toObjectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [_array count])]
forKeyPath:rest
@ -470,7 +467,8 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
return;
_NSObservationProxy *proxy=[[_NSObservationProxy alloc] initWithKeyPath:keyPath
observer:observer];
observer:observer
object:self];
int idx=[_observationProxies indexOfObject:proxy];
[proxy release];
proxy=[_observationProxies objectAtIndex:idx];
@ -494,12 +492,13 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
@end
@implementation _NSObservationProxy
-(id)initWithKeyPath:(id)keyPath observer:(id)observer
-(id)initWithKeyPath:(id)keyPath observer:(id)observer object:(id)object
{
if(self=[super init])
{
_keyPath=[keyPath retain];
_observer=observer;
_object=object;
}
return self;
}
@ -525,7 +524,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
if([other isMemberOfClass:isa])
{
_NSObservationProxy *o=other;
if(o->_observer==_observer && [o->_keyPath isEqual:_keyPath])
if(o->_observer==_observer && [o->_keyPath isEqual:_keyPath] && [o->_object isEqual:_object])
return YES;
}
return NO;
@ -537,7 +536,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
context:(void *)context
{
[_observer observeValueForKeyPath:_keyPath
ofObject:object
ofObject:_object
change:change
context:context];
}

View File

@ -22,10 +22,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
@implementation _NSBinder (BindingOptions)
-(BOOL)conditionallySetsEditable
{
// FIX: needs to read from options
if([source respondsToSelector:@selector(setEditable:)])
return YES;
return NO;
return [[options objectForKey:NSConditionallySetsEditableBindingOption] boolValue];
}
-(BOOL)conditionallySetsEnabled
@ -68,6 +65,14 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
return ret;
}
-(id)nullPlaceholder
{
id ret=[options objectForKey:NSNullPlaceholderBindingOption];
if(!ret)
return NSNoSelectionMarker;
return ret;
}
-(id)valueTransformer
{
id ret=[options objectForKey:NSValueTransformerBindingOption];

View File

@ -131,16 +131,24 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
id newValue=[self destinationValue];
BOOL editable=YES;
BOOL isPlaceholder=NO;
if(newValue==NSMultipleValuesMarker)
{
newValue=[self multipleValuesPlaceholder];
if(![self allowsEditingMultipleValues])
editable=NO;
isPlaceholder=YES;
}
else if(newValue==NSNoSelectionMarker)
{
newValue=[self noSelectionPlaceholder];
editable=NO;
isPlaceholder=YES;
}
else if(!newValue || newValue==[NSNull null])
{
newValue=[self nullPlaceholder];
isPlaceholder=YES;
}
if([self conditionallySetsEditable])
@ -150,6 +158,13 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
[source setValue:newValue
forKeyPath:bindingPath];
if(isPlaceholder && [source respondsToSelector:@selector(_setCurrentValueIsPlaceholder:)])
[source _setCurrentValueIsPlaceholder:YES];
}
else
{
NSLog(@"unexpected change notification for object %@ (src %@, dest %@)", object, source, destination);
}
[self startObservingChanges];

View File

@ -49,6 +49,7 @@ APPKIT_EXPORT NSString* NSObservedKeyPathKey;
APPKIT_EXPORT NSString* NSOptionsKey;
// Binding option keys
APPKIT_EXPORT NSString *NSNullPlaceholderBindingOption;
APPKIT_EXPORT NSString *NSNoSelectionPlaceholderBindingOption;
APPKIT_EXPORT NSString *NSMultipleValuesPlaceholderBindingOption;
APPKIT_EXPORT NSString *NSCreatesSortDescriptorBindingOption;
@ -56,4 +57,6 @@ APPKIT_EXPORT NSString *NSRaisesForNotApplicableKeysBindingOption;
APPKIT_EXPORT NSString *NSAllowsEditingMultipleValuesSelectionBindingOption;
APPKIT_EXPORT NSString *NSValueTransformerNameBindingOption;
APPKIT_EXPORT NSString *NSValueTransformerBindingOption;
APPKIT_EXPORT NSString *NSConditionallySetsEnabledBindingOption;
APPKIT_EXPORT NSString *NSConditionallySetsEditableBindingOption;

View File

@ -24,13 +24,16 @@ NSString* NSObservedObjectKey=@"NSObservedObject";
NSString* NSObservedKeyPathKey=@"NSObservedKeyPath";
NSString* NSOptionsKey=@"NSOptions";
NSString *NSNullPlaceholderBindingOption=@"NSNullPlaceholder";
NSString *NSNoSelectionPlaceholderBindingOption=@"NSNoSelectionPlaceholder";
NSString *NSMultipleValuesPlaceholderBindingOption=@"NSMultipleValuesPlaceholder";
NSString *NSCreatesSortDescriptorBindingOption=@"NSCreatesSortDescriptors";
NSString *NSRaisesForNotApplicableKeysBindingOption=@"NSRaisesForNotApplicableKeys";
NSString *NSAllowsEditingMultipleValuesSelectionBindingOption=@"NSAllowsEditingMultipleValuesSelection";
NSString *NSValueTransformerNameBindingOption=@"NSValueTransformerName";
NSString *NSValueTransformerBindingOption=@"NSValueTransformerBindingOption";
NSString *NSValueTransformerBindingOption=@"NSValueTransformerBinding";
NSString *NSConditionallySetsEnabledBindingOption=@"NSConditionallySetsEnabled";
NSString *NSConditionallySetsEditableBindingOption=@"NSConditionallySetsEditable";
@implementation NSObject (BindingSupport)

View File

@ -160,6 +160,22 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
return [self _kvo_operator_count];
}
-(id)_kvo_operator_sum:(NSString*)parameter
{
NSArray* objects=[self valueForKeyPath:parameter];
int count=[objects count];
int i;
double sum=0.0;
for(i=0; i<count; i++)
{
id obj=[objects objectAtIndex:i];
sum+=[obj doubleValue];
}
return [NSNumber numberWithDouble:sum];
}
-(void)setValue:(id)value forKey:(NSString*)key
{
id en=[self objectEnumerator];

View File

@ -323,7 +323,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
- (BOOL)validateValue:(id *)ioValue forKeyPath:(NSString *)keyPath error:(NSError **)outError
{
// FIX: should this be recursive or not?
id array=[[[keyPath componentsSeparatedByString:@"."] mutableCopy] autorelease];
id lastPathComponent=[array lastObject];
[array removeObject:lastPathComponent];

View File

@ -16,6 +16,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
id changeDictionary;
int willChangeCount;
NSString* keyPath;
id object;
}
-(id)observer;
- (id)changeDictionary;

View File

@ -116,6 +116,7 @@ static NSLock *kvoLock=nil;
info->observer=observer;
info->options=options;
info->context=context;
info->object=self;
[info setKeyPath:keyPath];
// if observing a key path, also observe all deeper levels
@ -814,7 +815,7 @@ CHANGE_DECLARATION(SEL)
[super dealloc];
}
-(void)observeValueForKeyPath:(NSString*)subKeyPath ofObject:(id)object change:(NSDictionary*)changeDict context:(void*)subContext;
-(void)observeValueForKeyPath:(NSString*)subKeyPath ofObject:(id)subObject change:(NSDictionary*)changeDict context:(void*)subContext;
{
[observer observeValueForKeyPath:keyPath
ofObject:object

View File

@ -29,3 +29,5 @@ FOUNDATION_EXPORT IMP OBJCInitializeLookupAndCacheUniqueIdForObject(id object,SE
FOUNDATION_EXPORT void OBJCLinkClassTable(void);
BOOL object_cxxConstruct(id self, Class c);
BOOL object_cxxDestruct(id self, Class c);

View File

@ -324,6 +324,55 @@ IMP OBJCInitializeLookupAndCacheUniqueIdForObject(id object,SEL uniqueId){
}
}
static inline BOOL OBJCCallCXXSelector(id self, Class class, SEL selector)
{
struct objc_method *result=NULL;
if(!class->super_class)
return YES;
if(!OBJCCallCXXSelector(self, class->super_class, selector))
return NO;
if((result=OBJCLookupUniqueIdInOnlyThisClass(class,selector))!=NULL)
{
if(result->method_imp(self, selector))
{
return YES;
}
else
{
object_cxxDestruct(self, class->super_class);
return NO;
}
}
return YES;
}
BOOL object_cxxConstruct(id self, Class class)
{
static SEL cxx_constructor=0;
if(!cxx_constructor)
cxx_constructor=sel_registerName(".cxx_construct");
if(!self)
return YES;
return OBJCCallCXXSelector(self, class, cxx_constructor);
}
BOOL object_cxxDestruct(id self, Class class)
{
return;
static SEL cxx_destructor=0;
if(!cxx_destructor)
cxx_destructor=sel_registerName(".cxx_destruct");
if(!self)
return YES;
return OBJCCallCXXSelector(self, class, cxx_destructor);
}
void OBJCLogMsg(id object,SEL message){
#if 1
if(object==nil)

View File

@ -62,11 +62,19 @@ id NSAllocateObject(Class class,unsigned extraBytes,NSZone *zone) {
result=calloc(1,class->instance_size+extraBytes);
result->isa=class;
if(!object_cxxConstruct(result, object->isa))
{
free(result);
result=nil;
}
return result;
}
void NSDeallocateObject(id object) {
object_cxxDestruct(object, object->isa);
if(NSZombieEnabled)
NSRegisterZombie(object);
else

View File

@ -92,11 +92,19 @@ id NSAllocateObject(Class class,unsigned extraBytes,NSZone *zone) {
result->isa=class;
//OBJCLog("allocated instance of %s at %d",class->name,result);
if(!object_cxxConstruct(result, result->isa))
{
HeapFree(zone,0,result);
result=nil;
}
return result;
}
void NSDeallocateObject(id object) {
object_cxxDestruct(object, object->isa);
if(NSZombieEnabled)
NSRegisterZombie(object);
else {