我花了最后一天寻找动态存储问题,在路径结束时我不知道发生了什么,除了我必须误解/遗漏了关于子类化NSString的事情。这是一个有很多问题的示例:
IDStringBug.h包含:
#import <Foundation/NSArray.h>
#import <Foundation/NSString.h>
/*==================================*/
@interface IDStringBug:NSString {
NSString *_backingStore;
NSArray *path;
}
- (NSArray*) path;
- (void) dealloc;
- (NSUInteger) length;
-(id) initWithString: (NSString*) string;
-(unichar) characterAtIndex:(NSUInteger) index;
@end
IDStringBug.m包含:
#include <stdio.h>
#import "IDStringBug.h"
@implementation IDStringBug
- (NSArray*) path {
printf ("Return ptr to IDString: %s\n", [_backingStore cString]);
return path;}
- (void) dealloc {
printf ("Release IDString: %s\n", [_backingStore cString]);
printf ("Path count is %d\n", (int) [path retainCount]);
[_backingStore release];
printf ("Apres _backinstore\n");
printf ("Path count is %d\n", (int) [path retainCount]);
[path release];
printf ("After path release, done but for super\n");
[super dealloc];
}
-(id)initWithString:(NSString*)string {
if ((self = [self init])) {
_backingStore = [[NSString stringWithString:string] copy];
}
path = [_backingStore componentsSeparatedByString: @"."];
printf ("Path count is %d\n", (int) [path retainCount]);
return self;
}
-(NSUInteger) length {
return [_backingStore length];
}
-(unichar)characterAtIndex:(NSUInteger)index {
return [_backingStore characterAtIndex:index];
}
@end
bug.m包含:
#include <stdio.h>
#include <Foundation/NSAutoreleasePool.h>
#import "IDStringBug.h"
int main(int argc, char* argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
IDStringBug *myids = [IDStringBug stringWithString: @"a.b.c"];
printf ("Path count is %d\n", (int) [[myids path] retainCount]);
printf ("pool=%d\n", (int) [pool autoreleaseCount]);
[pool release];
}
输出结果为:
$ ./bug
Path count is 1
Return ptr to IDString: a.b.c
Path count is 1
pool=7
Release IDString: a.b.c
Segmentation fault (core dumped)
答案 0 :(得分:6)
这个答案并没有直接解决你的问题,但它会间接解决它,并导致更易于维护的设计模式。
不要继承NSString。
相反,请使用合成。
@interface PathString:NSObject
@property(copy) NSString *stringValue;
@property(strong) NSArray *pathValue;
... etc ...
@end
实际崩溃是这样的:
path = [_backingStore componentsSeparatedByString: @"."];
该方法返回一个自动释放的对象,当池耗尽时会释放它,留下一个悬空引用。
正如其他人所说,retainCount
是utterly useless。
请注意,这很奇怪:
_backingStore = [[NSString stringWithString:string] copy];
那应该只是:
_backingStore = [string copy];
您的代码技术上复制字符串两次。我在技术上说,因为 - 由于实施细节,_backingStore
最终会指向string
(假设string
是NSString
而不是NSMutableString
}。
在NeXT时代,我非常喜欢内幕,但主要是 离开或仅使用Objc基地。
啊哈!我也是,1989年开始了ObjC编程。
这可以解释你来自哪里!
而不是retainCount
,这样的问题很容易使用僵尸进行调试。您可以在Xcode中的方案的选项窗格中打开它。
“whentouseretaincount.com”网站链接到article I wrote有关保留计数的信息。您可能会发现它很有趣,因为它通常也会阐明内存管理的一些细节。
Apple文档到Linux GnuStep世界
在您的问题中,这也是至关重要的。 GNUStep大多就像Apple的东西,但是更接近OpenStep世界。我不记得GNUStep是否有僵尸检测,但我怀疑它会。 Linux还有其他非常强大的内存调试工具。
retainCount
仍然会充满脆弱,但在处理单线程命令行工具时它会更稳定一些。不过,你仍然需要注意自动释放的东西。
答案 1 :(得分:2)
对componentsSeparatedByString:
的调用会返回已自动释放的NSArray
。这意味着它的保留计数为1,但是一旦自动释放池耗尽,该计数就会减少。将它与IDStringBug dealloc中的release
调用相结合,你会发现该数组被释放的次数太多了。
事实上,在调用IDStringBug dealloc时,路径数组已经被释放。因此,当您尝试确定保留计数时(通过调用[path retainCount]
,您尝试访问不再存在的对象。