diff --git a/ChangeLog b/ChangeLog index 9bc22c924..2c19bf1d2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,13 @@ 2002-08-07 Richard Frith-Macdonald * Source/NSString.m: ([-boolValue]) Accept 'true' as well as 'YES' + * Source/NSThread.m: Modified to avoid using non-standard notification + initialisation. + * Source/NSNotification.m: Rewritten as abstract/cluster class for + implementation compatibility with MacOS-X + * Source/NSNotificationCenter.m: Modified posting to behave like the + MacOS-X implementation. Implement concrete subclass of + NSNotification. 2002-07-29 Adam Fedor diff --git a/Headers/gnustep/base/NSNotification.h b/Headers/gnustep/base/NSNotification.h index c1f4e3044..390588a43 100644 --- a/Headers/gnustep/base/NSNotification.h +++ b/Headers/gnustep/base/NSNotification.h @@ -37,9 +37,6 @@ @interface NSNotification : NSObject { - NSString *_name; - id _object; - NSDictionary *_info; } /* Creating a Notification Object */ @@ -87,11 +84,6 @@ @end #ifndef NO_GNUSTEP -@interface NSNotification (GNUstep) -- (id) initWithName: (NSString*)name - object: (id)object - userInfo: (NSDictionary*)info; -@end @interface NSNotificationCenter (GNUstep) /* diff --git a/Source/NSNotification.m b/Source/NSNotification.m index d1837df60..10d52f77e 100644 --- a/Source/NSNotification.m +++ b/Source/NSNotification.m @@ -29,78 +29,110 @@ #include #include -@implementation NSNotification (GNUstep) -/* - * Initialise a newly created notification. - */ -- (id) initWithName: (NSString*)name - object: (id)object - userInfo: (NSDictionary*)info -{ - _name = [name copyWithZone: GSObjCZone(self)]; - _object = TEST_RETAIN(object); - _info = TEST_RETAIN(info); - return self; -} - -@end - @implementation NSNotification +static Class concreteClass = 0; +@class GSNotification; + ++ (NSNotification*) allocWithZone: (NSZone*)z +{ + return (id)NSAllocateObject(concreteClass, 0, z); +} + ++ (void) initialize +{ + if (concreteClass == 0) + { + concreteClass = [GSNotification class]; + } +} + +/** + * Create a new autoreleased notification. Concrete subclasses override + * this method to create actual notification objects. + */ + (NSNotification*) notificationWithName: (NSString*)name object: (id)object userInfo: (NSDictionary*)info { - return AUTORELEASE([[self allocWithZone: NSDefaultMallocZone()] - initWithName: name object: object userInfo: info]); + return [concreteClass notificationWithName: name + object: object + userInfo: info]; } +/** + * Create a new autoreleased notification by calling + * +notificationWithName:object:userInfo: with a nil user info argument. + */ + (NSNotification*) notificationWithName: (NSString*)name object: (id)object { - return [self notificationWithName: name object: object userInfo: nil]; + return [concreteClass notificationWithName: name + object: object + userInfo: nil]; } - +/** + * The abstract class implements a copy as a simple retain ... + * subclasses should override this to perform more intelligent + * copy operations. + */ - (id) copyWithZone: (NSZone*)zone { - if (NSShouldRetainWithZone (self, zone)) - { - return [self retain]; - } - return [[[self class] allocWithZone: zone] initWithName: _name - object: _object - userInfo: _info]; -} - -- (void) dealloc -{ - RELEASE(_name); - TEST_RELEASE(_object); - TEST_RELEASE(_info); - [super dealloc]; + return [self retain]; } +/** + * Return a description of the parts of the notification. + */ - (NSString*) description { return [[super description] stringByAppendingFormat: - @" Name: %@ Object: %@ Info: %@", _name, _object, _info]; + @" Name: %@ Object: %@ Info: %@", + [self name], [self object], [self userInfo]]; } +- (id) init +{ + if ([self class] == [NSNotification class]) + { + NSZone *z = [self zone]; + + RELEASE(self); + self = (id)NSAllocateObject (concreteClass, 0, z); + } + return self; +} + +/** + * Concrete subclasses of NSNotification are responsible for + * implementing this method to return the notification name. + */ - (NSString*) name { - return _name; + [self subclassResponsibility: _cmd]; + return nil; } +/** + * Concrete subclasses of NSNotification are responsible for + * implementing this method to return the notification object. + */ - (id) object { - return _object; + [self subclassResponsibility: _cmd]; + return nil; } +/** + * Concrete subclasses of NSNotification are responsible for + * implementing this method to return the notification user information. + */ - (NSDictionary*) userInfo { - return _info; + [self subclassResponsibility: _cmd]; + return nil; } /* @@ -110,17 +142,32 @@ */ - (void) encodeWithCoder: (NSCoder*)aCoder { - [aCoder encodeValueOfObjCType: @encode(id) at: &_name]; - [aCoder encodeValueOfObjCType: @encode(id) at: &_object]; - [aCoder encodeValueOfObjCType: @encode(id) at: &_info]; + id o; + + o = [self name]; + [aCoder encodeValueOfObjCType: @encode(id) at: &o]; + o = [self object]; + [aCoder encodeValueOfObjCType: @encode(id) at: &o]; + o = [self userInfo]; + [aCoder encodeValueOfObjCType: @encode(id) at: &o]; } - (id) initWithCoder: (NSCoder*)aCoder { - [aCoder decodeValueOfObjCType: @encode(id) at: &_name]; - [aCoder decodeValueOfObjCType: @encode(id) at: &_object]; - [aCoder decodeValueOfObjCType: @encode(id) at: &_info]; - return self; + NSString *name; + id object; + NSDictionary *info; + id n; + + [aCoder decodeValueOfObjCType: @encode(id) at: &name]; + [aCoder decodeValueOfObjCType: @encode(id) at: &object]; + [aCoder decodeValueOfObjCType: @encode(id) at: &info]; + n = [NSNotification notificationWithName: name object: object userInfo: info]; + RELEASE(name); + RELEASE(object); + RELEASE(info); + RELEASE(self); + return RETAIN(n); } @end diff --git a/Source/NSNotificationCenter.m b/Source/NSNotificationCenter.m index 8862c33c8..605972651 100644 --- a/Source/NSNotificationCenter.m +++ b/Source/NSNotificationCenter.m @@ -34,9 +34,84 @@ #include #include -typedef struct { - @defs(NSNotification) -} NotificationStruct; + +/** + * Concrete class implementing NSNotification + */ +@interface GSNotification : NSNotification +{ +@public + NSString *_name; + id _object; + NSDictionary *_info; +} +@end + +@implementation GSNotification + +static Class concrete = 0; + ++ (void) initialize +{ + if (concrete == 0) + { + concrete = [GSNotification class]; + } +} + ++ (NSNotification*) notificationWithName: (NSString*)name + object: (id)object + userInfo: (NSDictionary*)info +{ + GSNotification *n; + + n = (GSNotification*)NSAllocateObject(self, 0, NSDefaultMallocZone()); + n->_name = [name copyWithZone: GSObjCZone(self)]; + n->_object = TEST_RETAIN(object); + n->_info = TEST_RETAIN(info); + return AUTORELEASE(n); +} + +- (id) copyWithZone: (NSZone*)zone +{ + GSNotification *n; + + if (NSShouldRetainWithZone (self, zone)) + { + return [self retain]; + } + n = (GSNotification*)NSAllocateObject(concrete, 0, NSDefaultMallocZone()); + n->_name = [_name copyWithZone: GSObjCZone(self)]; + n->_object = TEST_RETAIN(_object); + n->_info = TEST_RETAIN(_info); + return n; +} + +- (void) dealloc +{ + RELEASE(_name); + TEST_RELEASE(_object); + TEST_RELEASE(_info); + [super dealloc]; +} + +- (NSString*) name +{ + return _name; +} + +- (id) object +{ + return _object; +} + +- (NSDictionary*) userInfo +{ + return _info; +} + +@end + /* * Garbage collection considerations - @@ -485,6 +560,10 @@ static NSNotificationCenter *default_center = nil; { if (self == [NSNotificationCenter class]) { + if (concrete == 0) + { + concrete = [GSNotification class]; + } /* * Do alloc and init separately so the default center can refer to * the 'default_center' variable during initialisation. @@ -854,35 +933,58 @@ static NSNotificationCenter *default_center = nil; } -/* - * Post NOTIFICATION to all the observers that match its NAME and OBJECT. - * +/** + * Posts notification to all the observers that match its NAME and OBJECT.
+ * The GNUstep implementation calls -postNotificationName:object:userInfo: to + * perform the actual posting. + */ +- (void) postNotification: (NSNotification*)notification +{ + [self postNotificationName: [notification name] + object: [notification object] + userInfo: [notification userInfo]]; +} + +/** + * Creates and posts a notification using the + * -postNotificationName:object:userInfo: passing a nil user info argument. + */ +- (void) postNotificationName: (NSString*)name + object: (id)object +{ + [self postNotificationName: name object: object userInfo: nil]; +} + +/** + * The preferred method for posting a notification. + *
* For performance reasons, we don't wrap an exception handler round every * message sent to an observer. This means that, if one observer raises * an exception, later observers in the lists will not get the notification. */ -- (void) postNotification: (NSNotification*)notification +- (void) postNotificationName: (NSString*)name + object: (id)object + userInfo: (NSDictionary*)info { - NSString *n_name; - id n_object; Observation *o; unsigned count; volatile GSIArray a; unsigned arrayBase; + GSNotification *notification; - if (notification == nil) - [NSException raise: NSInvalidArgumentException - format: @"Tried to post a nil notification."]; - - n_name = ((NotificationStruct*)notification)->_name; - n_object = ((NotificationStruct*)notification)->_object; - if (n_object != nil) - n_object = CHEATGC(n_object); - - if (n_name == nil) - [NSException raise: NSInvalidArgumentException - format: @"Tried to post a notification with no name."]; - + if (name == nil) + { + [NSException raise: NSInvalidArgumentException + format: @"Tried to post a notification with no name."]; + } + notification = (id)NSAllocateObject(concrete, 0, NSDefaultMallocZone()); + name = notification->_name = [name copyWithZone: GSObjCZone(self)]; + object = notification->_object = TEST_RETAIN(object); + notification->_info = TEST_RETAIN(info); + if (object != nil) + { + object = CHEATGC(object); + } lockNCTable(TABLE); a = ARRAY; @@ -922,9 +1024,9 @@ static NSNotificationCenter *default_center = nil; * Post the notification to all the observers that specified OBJECT, * but didn't specify NAME. */ - if (n_object) + if (object) { - n = GSIMapNodeForSimpleKey(NAMELESS, (GSIMapKey)n_object); + n = GSIMapNodeForSimpleKey(NAMELESS, (GSIMapKey)object); if (n != 0) { o = n->value.ext; @@ -941,19 +1043,23 @@ static NSNotificationCenter *default_center = nil; * observers with a non-nil OBJECT that doesn't match the * notification's OBJECT). */ - if (n_name) + if (name) { - n = GSIMapNodeForKey(NAMED, (GSIMapKey)n_name); + n = GSIMapNodeForKey(NAMED, (GSIMapKey)name); if (n) - m = (GSIMapTable)n->value.ptr; + { + m = (GSIMapTable)n->value.ptr; + } else - m = 0; + { + m = 0; + } if (m != 0) { /* * First, observers with a matching object. */ - n = GSIMapNodeForSimpleKey(m, (GSIMapKey)n_object); + n = GSIMapNodeForSimpleKey(m, (GSIMapKey)object); if (n != 0) { o = n->value.ext; @@ -965,7 +1071,7 @@ static NSNotificationCenter *default_center = nil; } } - if (n_object != nil) + if (object != nil) { /* * Now observers with a nil object. @@ -1008,9 +1114,9 @@ static NSNotificationCenter *default_center = nil; * Post the notification to all the observers that specified OBJECT, * but didn't specify NAME. */ - if (n_object) + if (object) { - n = GSIMapNodeForSimpleKey(NAMELESS, (GSIMapKey)n_object); + n = GSIMapNodeForSimpleKey(NAMELESS, (GSIMapKey)object); if (n != 0) { o = n->value.ext; @@ -1035,19 +1141,23 @@ static NSNotificationCenter *default_center = nil; * observers with a non-nil OBJECT that doesn't match the * notification's OBJECT). */ - if (n_name) + if (name) { - n = GSIMapNodeForKey(NAMED, (GSIMapKey)n_name); + n = GSIMapNodeForKey(NAMED, (GSIMapKey)name); if (n) - m = (GSIMapTable)n->value.ptr; + { + m = (GSIMapTable)n->value.ptr; + } else - m = 0; + { + m = 0; + } if (m != 0) { /* * First, observers with a matching object. */ - n = GSIMapNodeForSimpleKey(m, (GSIMapKey)n_object); + n = GSIMapNodeForSimpleKey(m, (GSIMapKey)object); if (n != 0) { o = n->value.ext; @@ -1058,7 +1168,7 @@ static NSNotificationCenter *default_center = nil; } } - if (n_object != nil) + if (object != nil) { /* * Now observers with a nil object. @@ -1097,28 +1207,14 @@ static NSNotificationCenter *default_center = nil; GSIArrayRemoveItemsFromIndex(ARRAY, arrayBase); unlockNCTable(TABLE); + DESTROY(notification); // Get rid of notification we created. [localException raise]; } NS_ENDHANDLER #endif unlockNCTable(TABLE); -} - -- (void) postNotificationName: (NSString*)name - object: (id)object -{ - [self postNotification: [NSNotification notificationWithName: name - object: object]]; -} - -- (void) postNotificationName: (NSString*)name - object: (id)object - userInfo: (NSDictionary*)info -{ - [self postNotification: [NSNotification notificationWithName: name - object: object - userInfo: info]]; + RELEASE(notification); } @end diff --git a/Source/NSThread.m b/Source/NSThread.m index 586ea90c6..d3eca01c6 100644 --- a/Source/NSThread.m +++ b/Source/NSThread.m @@ -41,6 +41,7 @@ #include static Class threadClass = Nil; +static NSNotificationCenter *nc = nil; #ifndef NO_GNUSTEP #if !defined(HAVE_OBJC_THREAD_ADD) && !defined(NeXT_RUNTIME) @@ -156,15 +157,14 @@ gnustep_base_thread_callback() */ if (entered_multi_threaded_state == NO) { - NSNotification *n; - entered_multi_threaded_state = YES; - n = [NSNotification alloc]; - n = [n initWithName: NSWillBecomeMultiThreadedNotification - object: nil - userInfo: nil]; - [[NSNotificationCenter defaultCenter] postNotification: n]; - RELEASE(n); + if (nc == nil) + { + nc = [NSNotificationCenter defaultCenter]; + } + [nc postNotificationName: NSWillBecomeMultiThreadedNotification + object: nil + userInfo: nil]; } } @@ -248,8 +248,6 @@ gnustep_base_thread_callback() t = GSCurrentThread(); if (t->_active == YES) { - NSNotification *n; - /* * Set the thread to be inactive to avoid any possibility of recursion. */ @@ -258,12 +256,13 @@ gnustep_base_thread_callback() /* * Let observers know this thread is exiting. */ - n = [NSNotification alloc]; - n = [n initWithName: NSThreadWillExitNotification - object: t - userInfo: nil]; - [[NSNotificationCenter defaultCenter] postNotification: n]; - RELEASE(n); + if (nc == nil) + { + nc = [NSNotificationCenter defaultCenter]; + } + [nc postNotificationName: NSThreadWillExitNotification + object: t + userInfo: nil]; /* * destroy the thread object. @@ -413,10 +412,6 @@ gnustep_base_thread_callback() - (void) _sendThreadMethod { -#ifndef NO_GNUSTEP - NSNotification *n; -#endif - /* * We are running in the new thread - so we store ourself in the thread * dictionary and release ourself - thus, when the thread exits, we will @@ -429,12 +424,13 @@ gnustep_base_thread_callback() /* * Let observers know a new thread is starting. */ - n = [NSNotification alloc]; - n = [n initWithName: NSThreadDidStartNotification - object: self - userInfo: nil]; - [[NSNotificationCenter defaultCenter] postNotification: n]; - RELEASE(n); + if (nc == nil) + { + nc = [NSNotificationCenter defaultCenter]; + } + [nc postNotificationName: NSThreadDidStartNotification + object: self + userInfo: nil]; #endif [_target performSelector: _selector withObject: _arg]; @@ -517,21 +513,11 @@ void GSUnregisterCurrentThread (void) { NSThread *thread; - static NSNotificationCenter *nc = nil; - static Class notificationClass = Nil; - if (nc == nil) - { - nc = [NSNotificationCenter defaultCenter]; - notificationClass = [NSNotification class]; - } - thread = GSCurrentThread(); if (((NSThread_ivars *)thread)->_active == YES) { - NSNotification *n; - /* * Set the thread to be inactive to avoid any possibility of recursion. */ @@ -540,12 +526,13 @@ GSUnregisterCurrentThread (void) /* * Let observers know this thread is exiting. */ - n = [notificationClass alloc]; - n = [n initWithName: NSThreadWillExitNotification - object: thread - userInfo: nil]; - [nc postNotification: n]; - RELEASE(n); + if (nc == nil) + { + nc = [NSNotificationCenter defaultCenter]; + } + [nc postNotificationName: NSThreadWillExitNotification + object: thread + userInfo: nil]; /* * destroy the thread object.