diff --git a/ChangeLog b/ChangeLog index 5f8176127..1e60edcf1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,25 @@ +2003-04-17 Richard Frith-Macdonald + + * Headers/gnustep/base/GSCategories.h: + * Headers/gnustep/base/NSCalendarDate.h: + * Headers/gnustep/base/NSData.h: + * Headers/gnustep/base/NSObject.h: + * Headers/gnustep/base/NSString.h: + * Headers/gnustep/base/NSValue.h: + * Headers/gnustep/base/Foundation.h: + Tidied use of GSCategories. + +2003-04-16 Richard Frith-Macdonald + + * Headers/gnustep/base/NSUserDefaults.h: remove timer ivar, now unused. + * Source/NSRunLoop.m: Support a housekeeping timer which is ignored + for purposes of deciding whether the loop shoiuld terminate. + * Source/NSThread.m: Set up housekeeping timer to trigger housekeeping + notifications in the default mode of the runloop of the main thread. + * Source/NSUserDefaults.m: Use housekeeping notifications to trigger + synchronise rather than using timers ... avoid circular dependencies. + Thanks to Derek Zhou for bug report. + 2003-04-15 Richard Frith-Macdonald * Source/NSData.m: Experimantal disable ReadFile and WriteFile on MINGW diff --git a/Headers/gnustep/base/Foundation.h b/Headers/gnustep/base/Foundation.h index 9c5ad268a..5c7027c06 100644 --- a/Headers/gnustep/base/Foundation.h +++ b/Headers/gnustep/base/Foundation.h @@ -90,7 +90,5 @@ #include #include #include -#include -#include #endif /* __Foundation_h_GNUSTEP_BASE_INCLUDE */ diff --git a/Headers/gnustep/base/GSCategories.h b/Headers/gnustep/base/GSCategories.h index f00a5842e..e236ee916 100644 --- a/Headers/gnustep/base/GSCategories.h +++ b/Headers/gnustep/base/GSCategories.h @@ -26,29 +26,26 @@ */ -#ifndef NO_GNUSTEP -#ifndef NeXT_Foundation_LIBRARY -#include -#include -#include -#include -#else +/* The following ifndef prevents the categories declared in this file being + * seen in GNUstep code. This is necessary because those category + * declarations are also present in the header files for the corresponding + * classes in GNUstep. The separate category declarations in this file + * are only needed for software using the GNUstep Additions library + * without the main GNUstep base library. + */ +#ifndef GNUSTEP + #include -#endif @interface NSCalendarDate (GSCategories) - - (int) weekOfYear; - @end @interface NSData (GSCategories) - - (NSString*) hexadecimalRepresentation; - (id) initWithHexadecimalRepresentation: (NSString*)string; - (NSData*) md5Digest; - @end @interface NSString (GSCategories) @@ -75,19 +72,17 @@ + (NSValue*) valueFromString: (NSString *)string; @end -/* This is also defined in NSObject.h, but added here for use with the - additions library */ -#ifndef NSOBJECT_GSCATEGORIES_INTERFACE @interface NSObject (GSCategories) - notImplemented:(SEL)aSel; - (id) subclassResponsibility: (SEL)aSel; - (id) shouldNotImplement: (SEL)aSel; - - (NSComparisonResult) compare: (id)anObject; @end -#endif +#endif /* GNUSTEP */ + + #ifndef GS_MAX_OBJECTS_FROM_STACK /** @@ -235,5 +230,4 @@ }) -#endif /* NO_GNUSTEP */ #endif /* INCLUDED_GS_CATEGORIES_H */ diff --git a/Headers/gnustep/base/NSCalendarDate.h b/Headers/gnustep/base/NSCalendarDate.h index ba61b98d3..ccc80abe4 100644 --- a/Headers/gnustep/base/NSCalendarDate.h +++ b/Headers/gnustep/base/NSCalendarDate.h @@ -105,6 +105,11 @@ @end #ifndef NO_GNUSTEP + +@interface NSCalendarDate (GSCategories) +- (int) weekOfYear; +@end + @interface NSCalendarDate (GregorianDate) - (int) lastDayOfGregorianMonth: (int)month year: (int)year; diff --git a/Headers/gnustep/base/NSData.h b/Headers/gnustep/base/NSData.h index b0733b811..82414d68e 100644 --- a/Headers/gnustep/base/NSData.h +++ b/Headers/gnustep/base/NSData.h @@ -116,6 +116,12 @@ #ifndef NO_GNUSTEP +@interface NSData (GSCategories) +- (NSString*) hexadecimalRepresentation; +- (id) initWithHexadecimalRepresentation: (NSString*)string; +- (NSData*) md5Digest; +@end + /* * We include special support for coding/decoding - adding methods for * serializing/deserializing type-tags and cross-references. @@ -250,6 +256,7 @@ @end #ifndef NO_GNUSTEP + @interface NSMutableData (GNUstepExtensions) /* * Capacity management - GNUstep gives you control over the size of diff --git a/Headers/gnustep/base/NSObject.h b/Headers/gnustep/base/NSObject.h index 4f15e64aa..8238f0472 100644 --- a/Headers/gnustep/base/NSObject.h +++ b/Headers/gnustep/base/NSObject.h @@ -291,12 +291,10 @@ GS_EXPORT NSRecursiveLock *gnustep_global_lock; - (id) write: (TypedStream*)aStream; @end -#define NSOBJECT_GSCATEGORIES_INTERFACE @interface NSObject (GSCategories) - notImplemented:(SEL)aSel; - (id) subclassResponsibility: (SEL)aSel; - (id) shouldNotImplement: (SEL)aSel; - - (NSComparisonResult) compare: (id)anObject; @end diff --git a/Headers/gnustep/base/NSString.h b/Headers/gnustep/base/NSString.h index 388dd3312..aacebdc7a 100644 --- a/Headers/gnustep/base/NSString.h +++ b/Headers/gnustep/base/NSString.h @@ -369,6 +369,27 @@ extern struct objc_class _NSConstantStringClassReference; @interface NSMutableString (GNUstep) - (NSString*) immutableProxy; @end + +@interface NSString (GSCategories) +- (NSString*) stringByDeletingPrefix: (NSString*)prefix; +- (NSString*) stringByDeletingSuffix: (NSString*)suffix; +- (NSString*) stringByTrimmingLeadSpaces; +- (NSString*) stringByTrimmingTailSpaces; +- (NSString*) stringByTrimmingSpaces; +- (NSString*) stringByReplacingString: (NSString*)replace + withString: (NSString*)by; +@end + +@interface NSMutableString (GSCategories) +- (void) deleteSuffix: (NSString*)suffix; +- (void) deletePrefix: (NSString*)prefix; +- (void) replaceString: (NSString*)replace + withString: (NSString*)by; +- (void) trimLeadSpaces; +- (void) trimTailSpaces; +- (void) trimSpaces; +@end + #endif /* NO_GNUSTEP */ #endif /* __NSString_h_GNUSTEP_BASE_INCLUDE */ diff --git a/Headers/gnustep/base/NSUserDefaults.h b/Headers/gnustep/base/NSUserDefaults.h index 260481d49..c06348582 100644 --- a/Headers/gnustep/base/NSUserDefaults.h +++ b/Headers/gnustep/base/NSUserDefaults.h @@ -107,7 +107,6 @@ GS_EXPORT NSString* const NSLocale; the M$ hell. God help the Win95/WinNT users of NSUserDefaults ;-) To Do: - - ask somebody to test it for M$; - polish & optimize; - when tested, fix NSBundle (the system languages stuff); - write docs : -( @@ -124,7 +123,6 @@ GS_EXPORT NSString* const NSLocale; NSDictionary *_dictionaryRep; // Cached dictionary representation NSString *_defaultsDatabase; NSDate *_lastSync; - NSTimer *_tickingTimer; // for synchronization NSRecursiveLock *_lock; NSDistributedLock *_fileLock; } @@ -168,7 +166,7 @@ GS_EXPORT NSString* const NSLocale; /* Returning the Search List */ - (NSMutableArray*) searchList; -- (void)setSearchList: (NSArray*)newList; +- (void) setSearchList: (NSArray*)newList; /* Maintaining Persistent Domains */ - (NSDictionary*) persistentDomainForName: (NSString*)domainName; diff --git a/Headers/gnustep/base/NSValue.h b/Headers/gnustep/base/NSValue.h index d4041f9ee..c5bcb9741 100644 --- a/Headers/gnustep/base/NSValue.h +++ b/Headers/gnustep/base/NSValue.h @@ -118,6 +118,11 @@ @end #ifndef NO_GNUSTEP + +@interface NSNumber(GSCategories) ++ (NSValue*) valueFromString: (NSString *)string; +@end + /* Note: This method is not in the OpenStep spec, but they makes subclassing easier. */ @interface NSValue (Subclassing) diff --git a/Source/NSInvocation.m b/Source/NSInvocation.m index 63b2a9f5b..1be612c7c 100644 --- a/Source/NSInvocation.m +++ b/Source/NSInvocation.m @@ -176,6 +176,8 @@ _arg_addr(NSInvocation *inv, int index) /** * Returns an invocation instance which can be used to send messages to * a target object using the described signature.
+ * You must set the target and selector (using -setTarget: and -setSelector:) + * before you attempt to use the invocation.
* Raises an NSInvalidArgumentException if the signature is nil. */ + (NSInvocation*) invocationWithMethodSignature: (NSMethodSignature*)_signature @@ -734,6 +736,8 @@ _arg_addr(NSInvocation *inv, int index) /** * Initialised an invocation instance which can be used to send messages to * a target object using aSignature.
+ * You must set the target and selector (using -setTarget: and -setSelector:) + * before you attempt to use the invocation.
* Raises an NSInvalidArgumentException if aSignature is nil. */ - (id) initWithMethodSignature: (NSMethodSignature*)aSignature diff --git a/Source/NSRunLoop.m b/Source/NSRunLoop.m index 70451e976..1d671089e 100644 --- a/Source/NSRunLoop.m +++ b/Source/NSRunLoop.m @@ -604,7 +604,6 @@ static void setPollfd(int fd, int event, GSRunLoopCtxt *ctxt) info = GSIArrayItemAtIndex(watchers, i).obj; if (info->_invalidated == YES) { - GSIArrayRemoveItemAtIndex(watchers, i); continue; } @@ -1622,7 +1621,7 @@ static void setPollfd(int fd, int event, GSRunLoopCtxt *ctxt) + (NSRunLoop*) currentRunLoop { - extern NSRunLoop *GSRunLoopForThread(); + extern NSRunLoop *GSRunLoopForThread(); return GSRunLoopForThread(nil); } @@ -1709,6 +1708,7 @@ static void setPollfd(int fd, int event, GSRunLoopCtxt *ctxt) */ - (NSDate*) limitDateForMode: (NSString*)mode { + extern NSTimer *GSHousekeeper(); GSRunLoopCtxt *context = NSMapGet(_contextMap, mode); NSDate *when = nil; @@ -1825,6 +1825,32 @@ static void setPollfd(int fd, int event, GSRunLoopCtxt *ctxt) min_watcher = nil; } } + + /* + * If there is nothing being watched, and no valid timers + * other than the housekeeper, we set min_timer to nil so + * that the housekeeper timer does not keep the runloop + * active. It's a special case set up in NSThread.m + */ + if (min_watcher == nil && min_timer != nil + && min_timer == GSHousekeeper()) + { + unsigned count = GSIArrayCount(timers); + + while (count-- > 1) + { + NSTimer *tmp = GSIArrayItemAtIndex(timers, 0).obj; + if (timerInvalidated(tmp) == YES) + { + GSIArrayRemoveItemAtIndex(timers, count); + } + } + if (GSIArrayCount(timers) == 1) + { + min_timer = nil; + } + } + _currentMode = savedMode; } NS_HANDLER diff --git a/Source/NSThread.m b/Source/NSThread.m index 6aa33ed5e..dd61bd7a3 100644 --- a/Source/NSThread.m +++ b/Source/NSThread.m @@ -42,6 +42,7 @@ #include #include #include +#include @class GSPerformHolder; @@ -197,6 +198,22 @@ GSCurrentThreadDictionary() return GSDictionaryForThread(nil); } +/* + * The special timer which we set up in the run loop of the main thread + * to perform housekeeping duties. NSRunLoop needs to call this private + * function so it knows about the housekeeping timer and won't keep the + * loop running just to do housekeeping. + * + * The NSUserDefaults system registers as an observer of GSHousekeeping + * notifications in order to synchronise the in-memory cache and the + * on-disk database. + */ +static NSTimer *housekeeper = nil; +NSTimer *GSHousekeeper() +{ + return housekeeper; +} + /** * Returns the runloop for the specified thread (or, if t is nil, * for the current thread). Creates a new runloop if necessary.
@@ -217,6 +234,30 @@ GSRunLoopForThread(NSThread *t) r = [NSRunLoop new]; [d setObject: r forKey: key]; RELEASE(r); + if (t == nil || t == defaultThread) + { + NSNotificationCenter *ctr; + NSNotification *not; + NSInvocation *inv; + SEL sel; + + ctr = [NSNotificationCenter defaultCenter]; + not = [NSNotification notificationWithName: @"GSHousekeeping" + object: r + userInfo: nil]; + sel = @selector(postNotification:); + inv = [NSInvocation invocationWithMethodSignature: + [ctr methodSignatureForSelector: sel]]; + [inv setTarget: ctr]; + [inv setSelector: sel]; + [inv setArgument: ¬ atIndex: 2]; + [inv retainArguments]; + + housekeeper = [NSTimer timerWithTimeInterval: 30.0 + invocation: inv + repeats: YES]; + [r addTimer: housekeeper forMode: NSDefaultRunLoopMode]; + } } } return r; diff --git a/Source/NSTimer.m b/Source/NSTimer.m index 04a631402..90859fe37 100644 --- a/Source/NSTimer.m +++ b/Source/NSTimer.m @@ -92,7 +92,7 @@ static Class NSDate_class; } /** - * Create a timer wchich will fire after ti seconds and, if f is YES, + * Create a timer which will fire after ti seconds and, if f is YES, * every ti seconds thereafter. On firing, invocation will be performed.
* NB. To make the timer operate, you must add it to a run loop. */ @@ -111,7 +111,7 @@ static Class NSDate_class; /** * Create a timer wchich will fire after ti seconds and, if f is YES, * every ti seconds thereafter. On firing, the target object will be - * sent a message specified by selector and with the object info as an + * sent a message specified by selector and with the value info as an * argument.
* NB. To make the timer operate, you must add it to a run loop. */ diff --git a/Source/NSUserDefaults.m b/Source/NSUserDefaults.m index 04fa382eb..c27080d4a 100644 --- a/Source/NSUserDefaults.m +++ b/Source/NSUserDefaults.m @@ -130,7 +130,6 @@ static void updateCache(NSUserDefaults *self) - (void) __createStandardSearchList; - (NSDictionary*) __createArgumentDictionary; - (void) __changePersistentDomain: (NSString*)domainName; -- (void) __timerTicked: (NSTimer*)tim; @end /** @@ -522,7 +521,8 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */ [tempDefaults setSearchList: sList]; RELEASE(sList); currLang = [tempDefaults stringArrayForKey: @"NSLanguages"]; - AUTORELEASE(tempDefaults); + AUTORELEASE(RETAIN(currLang)); + RELEASE(tempDefaults); } } else @@ -825,15 +825,17 @@ static NSString *pathForUser(NSString *user) setObject: [NSMutableDictionaryClass dictionaryWithCapacity: 10] forKey: NSRegistrationDomain]; + [[NSNotificationCenter defaultCenter] addObserver: self + selector: @selector(synchronize) + name: @"GSHousekeeping" + object: nil]; + return self; } - (void) dealloc { - if (_tickingTimer != nil) - { - [_tickingTimer invalidate]; - } + [[NSNotificationCenter defaultCenter] removeObserver: self]; RELEASE(_lastSync); RELEASE(_searchList); RELEASE(_persDomains); @@ -1364,15 +1366,6 @@ static BOOL isPlistObject(id o) } } - if (_tickingTimer == nil) - { - _tickingTimer = [NSTimer scheduledTimerWithTimeInterval: 30 - target: self - selector: @selector(__timerTicked:) - userInfo: nil - repeats: NO]; - } - /* * If we haven't changed anything, we only need to synchronise if * the on-disk database has been changed by someone else. @@ -1397,8 +1390,12 @@ static BOOL isPlistObject(id o) { NSDate *mod; + /* + * If the database was modified since the last synchronisation + * we need to read it. + */ mod = [attr objectForKey: NSFileModificationDate]; - if (mod !=nil && [_lastSync earlierDate: mod] != _lastSync) + if (mod != nil && [_lastSync laterDate: mod] != _lastSync) { wantRead = YES; } @@ -1806,14 +1803,6 @@ static BOOL isPlistObject(id o) } [_lock unlock]; } - -- (void) __timerTicked: (NSTimer*)tim -{ - if (tim == _tickingTimer) - _tickingTimer = nil; - - [self synchronize]; -} @end NSDictionary*