diff --git a/ChangeLog b/ChangeLog index a50972fe0..c3f403725 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,10 +4,15 @@ worked as expected and moved getpwnam() call to where it works. * Source/Additions/GSMime.m: ([GSMimeDocument-rawMimeData:]) bugfix to remove mjime version header from inner documents. + * Source/NSDebug.m: Additional functions for tracking memory problems. * Source/NSBundle.m: Fixup for windows. * Source/NSFileManager.m: Fixup for windows. * Source/NSString.m: Fixup for windows. Windows fixups adapted from patch by + * Source/NSArray.m: Fix memory leak initialising from coder. + * Source/NSCountedSet.m: ditto + * Source/NSSet.m: ditto + Memory leak fixes adapted from patch by 2003-10-07 Richard Frith-Macdonald diff --git a/Headers/Foundation/NSDebug.h b/Headers/Foundation/NSDebug.h index 8779031ac..cb286ed3e 100644 --- a/Headers/Foundation/NSDebug.h +++ b/Headers/Foundation/NSDebug.h @@ -132,6 +132,16 @@ GS_EXPORT void GSDebugAllocationActiveRecordingObjects(Class c); */ GS_EXPORT NSArray *GSDebugAllocationListRecordedObjects(Class c); +/** + * This function associates the supplied tag with a recorded + * object and returns the tag which was previously associated + * with it (if any).
+ * If the object was not recorded, the method reurns nil
+ * The tag is retained while it is associated with the object.
+ * See also the NSDebugFRLog() and NSDebugMRLog() macros. + */ +GS_EXPORT id GSDebugAllocationTagRecordedObject(id object, id tag); + /** * Used to produce a format string for logging a message with function * location details. @@ -288,6 +298,29 @@ GS_EXPORT BOOL NSDeallocateZombies; NSString *fmt = GSDebugMethodMsg( \ self, _cmd, __FILE__, __LINE__, format); \ NSLog(fmt , ## args); }} while (0) + +/** + * This macro saves the name and location of the function in + * which the macro is used, along with a short string msg as + * the tag associated with a recorded object. + */ +#define NSDebugFRLog(object, msg) \ + do { \ + NSString *tag = GSDebugFunctionMsg( \ + __PRETTY_FUNCTION__, __FILE__, __LINE__, msg); \ + GSDebugAllocationTagRecordedObject(object, tag); } while (0) + +/** + * This macro saves the name and location of the method in + * which the macro is used, along with a short string msg as + * the tag associated with a recorded object. + */ +#define NSDebugMRLog(object, msg) \ + do { \ + NSString *tag = GSDebugMethodMsg( \ + self, _cmd, __FILE__, __LINE__, msg); \ + GSDebugAllocationTagRecordedObject(object, tag); } while (0) + #else #define NSDebugLLog(level, format, args...) #define NSDebugLog(format, args...) @@ -295,6 +328,8 @@ GS_EXPORT BOOL NSDeallocateZombies; #define NSDebugFLog(format, args...) #define NSDebugMLLog(level, format, args...) #define NSDebugMLog(format, args...) +#define NSDebugFRLog(object, msg) +#define NSDebugMRLog(object, msg) #endif diff --git a/Source/NSArray.m b/Source/NSArray.m index 258220000..b0cca1cb9 100644 --- a/Source/NSArray.m +++ b/Source/NSArray.m @@ -552,12 +552,19 @@ static SEL rlSel; [aCoder decodeArrayOfObjCType: @encode(id) count: count at: contents]; - return [self initWithObjects: contents count: count]; + self = [self initWithObjects: contents count: count]; +#if GS_WITH_GC == 0 + while (count-- > 0) + { + [contents[count] release]; + } +#endif } else { - return [self initWithObjects: 0 count: 0]; + self = [self initWithObjects: 0 count: 0]; } + return self; } /** diff --git a/Source/NSCountedSet.m b/Source/NSCountedSet.m index b0d0516c7..a6a02224a 100644 --- a/Source/NSCountedSet.m +++ b/Source/NSCountedSet.m @@ -152,6 +152,7 @@ static Class NSCountedSet_concrete_class; while (j-- > 1) { (*addImp)(self, @selector(addObject:), objs[i]); + RELEASE(objs[i]); } } } diff --git a/Source/NSDebug.m b/Source/NSDebug.m index 28d8c3957..58665ad67 100644 --- a/Source/NSDebug.m +++ b/Source/NSDebug.m @@ -45,6 +45,7 @@ typedef struct { /* The following are used to record actual objects */ BOOL is_recording; id *recorded_objects; + id *recorded_tags; unsigned int num_recorded_objects; unsigned int stack_size; } table_entry; @@ -178,6 +179,7 @@ GSDebugAllocationActiveRecordingObjects(Class c) the_table[num_classes].peak = 0; the_table[num_classes].is_recording = YES; the_table[num_classes].recorded_objects = NULL; + the_table[num_classes].recorded_tags = NULL; the_table[num_classes].num_recorded_objects = 0; the_table[num_classes].stack_size = 0; num_classes++; @@ -206,21 +208,33 @@ GSDebugAllocationAdd(Class c, id o) } if (the_table[i].is_recording == YES) { - if (the_table[i].num_recorded_objects >= - the_table[i].stack_size) + if (the_table[i].num_recorded_objects + >= the_table[i].stack_size) { - int more = the_table[i].stack_size + 128; + int more = the_table[i].stack_size + 128; id *tmp; + id *tmp1; tmp = NSZoneMalloc(NSDefaultMallocZone(), more * sizeof(id)); - if (tmp == 0) { if (uniqueLock != nil) [uniqueLock unlock]; return; } + + tmp1 = NSZoneMalloc(NSDefaultMallocZone(), + more * sizeof(id)); + if (tmp1 == 0) + { + NSZoneFree(NSDefaultMallocZone(), tmp); + if (uniqueLock != nil) + [uniqueLock unlock]; + return; + } + + if (the_table[i].recorded_objects != NULL) { memcpy(tmp, the_table[i].recorded_objects, @@ -228,13 +242,21 @@ GSDebugAllocationAdd(Class c, id o) * sizeof(id)); NSZoneFree(NSDefaultMallocZone(), the_table[i].recorded_objects); + memcpy(tmp1, the_table[i].recorded_tags, + the_table[i].num_recorded_objects + * sizeof(id)); + NSZoneFree(NSDefaultMallocZone(), + the_table[i].recorded_tags); } the_table[i].recorded_objects = tmp; + the_table[i].recorded_tags = tmp1; the_table[i].stack_size = more; } (the_table[i].recorded_objects) [the_table[i].num_recorded_objects] = o; + (the_table[i].recorded_tags) + [the_table[i].num_recorded_objects] = nil; the_table[i].num_recorded_objects++; } if (uniqueLock != nil) @@ -272,6 +294,7 @@ GSDebugAllocationAdd(Class c, id o) the_table[num_classes].peak = 1; the_table[num_classes].is_recording = NO; the_table[num_classes].recorded_objects = NULL; + the_table[num_classes].recorded_tags = NULL; the_table[num_classes].num_recorded_objects = 0; the_table[num_classes].stack_size = 0; num_classes++; @@ -605,6 +628,8 @@ GSDebugAllocationRemove(Class c, id o) { if (the_table[i].class == c) { + id tag = nil; + if (uniqueLock != nil) [uniqueLock lock]; the_table[i].count--; @@ -616,6 +641,7 @@ GSDebugAllocationRemove(Class c, id o) { if ((the_table[i].recorded_objects)[j] == o) { + tag = (the_table[i].recorded_tags)[j]; break; } } @@ -627,6 +653,8 @@ GSDebugAllocationRemove(Class c, id o) { (the_table[i].recorded_objects)[k] = (the_table[i].recorded_objects)[k + 1]; + (the_table[i].recorded_tags)[k] = + (the_table[i].recorded_tags)[k + 1]; } the_table[i].num_recorded_objects--; } @@ -640,12 +668,67 @@ GSDebugAllocationRemove(Class c, id o) } if (uniqueLock != nil) [uniqueLock unlock]; + RELEASE(tag); return; } } } } +/** + * This function associates the supplied tag with a recorded + * object and returns the tag which was previously associated + * with it (if any).
+ * If the object was not recorded, the method returns nil
+ * The tag is retained while it is associated with the object. + */ +id +GSDebugAllocationTagRecordedObject(id object, id tag) +{ + Class c = [object class]; + id o = nil; + int i; + int j; + + if (debug_allocation == NO) + { + return nil; + } + if (uniqueLock != nil) + [uniqueLock lock]; + + for (i = 0; i < num_classes; i++) + { + if (the_table[i].class == c) + { + break; + } + } + + if (i == num_classes + || the_table[i].is_recording == NO + || the_table[i].num_recorded_objects == 0) + { + if (uniqueLock != nil) + [uniqueLock unlock]; + return nil; + } + + for (j = 0; j < the_table[i].num_recorded_objects; j++) + { + if (the_table[i].recorded_objects[j] == object) + { + o = the_table[i].recorded_tags[j]; + the_table[i].recorded_tags[j] = RETAIN(tag); + break; + } + } + + if (uniqueLock != nil) + [uniqueLock unlock]; + return AUTORELEASE(o); +} + /** * This function returns an array * containing all the allocated objects of a certain class @@ -702,7 +785,6 @@ GSDebugAllocationListRecordedObjects(Class c) tmp = NSZoneMalloc(NSDefaultMallocZone(), the_table[i].num_recorded_objects * sizeof(id)); - if (tmp == 0) { if (uniqueLock != nil) @@ -714,10 +796,6 @@ GSDebugAllocationListRecordedObjects(Class c) memcpy(tmp, the_table[i].recorded_objects, the_table[i].num_recorded_objects * sizeof(id)); - /* Then, we bravely unlock the lock */ - if (uniqueLock != nil) - [uniqueLock unlock]; - /* Retain all the objects - NB: if retaining one of the objects as a side effect releases another one of them , we are broken ... */ for (k = 0; k < the_table[i].num_recorded_objects; k++) @@ -725,6 +803,10 @@ GSDebugAllocationListRecordedObjects(Class c) RETAIN (tmp[k]); } + /* Then, we bravely unlock the lock */ + if (uniqueLock != nil) + [uniqueLock unlock]; + /* Only then we create an array with them - this is now safe as we have copied the objects out, unlocked, and retained them. */ answer = [NSArray arrayWithObjects: tmp diff --git a/Source/NSSet.m b/Source/NSSet.m index eb4a4e732..59eb3ba30 100644 --- a/Source/NSSet.m +++ b/Source/NSSet.m @@ -166,17 +166,26 @@ static Class NSMutableSet_concrete_class; self = [NSMutableSet_concrete_class allocWithZone: NSDefaultMallocZone()]; return [self initWithCoder: aCoder]; } - [aCoder decodeValueOfObjCType: @encode(unsigned) at: &count]; - { - id objs[count]; - unsigned i; - for (i = 0; i < count; i++) - { - [aCoder decodeValueOfObjCType: @encode(id) at: &objs[i]]; - } - return [self initWithObjects: objs count: count]; - } + [aCoder decodeValueOfObjCType: @encode(unsigned) at: &count]; + if (count > 0) + { + id objs[count]; + unsigned i; + + for (i = 0; i < count; i++) + { + [aCoder decodeValueOfObjCType: @encode(id) at: &objs[i]]; + } + self = [self initWithObjects: objs count: count]; +#if GS_WITH_GC == 0 + while (count-- > 0) + { + [objs[count] release]; + } +#endif + } + return self; } /*