子类化NSString的问题

时间:2013-07-14 16:58:02

标签: objective-c

我花了最后一天寻找动态存储问题,在路径结束时我不知道发生了什么,除了我必须误解/遗漏了关于子类化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)

2 个答案:

答案 0 :(得分:6)

这个答案并没有直接解决你的问题,但它会间接解决它,并导致更易于维护的设计模式。

不要继承NSString。

相反,请使用合成。

@interface PathString:NSObject
@property(copy) NSString *stringValue;
@property(strong) NSArray *pathValue;
... etc ...
@end

实际崩溃是这样的:

  path    = [_backingStore componentsSeparatedByString: @"."];

该方法返回一个自动释放的对象,当池耗尽时会释放它,留下一个悬空引用。

正如其他人所说,retainCountutterly useless

请注意,这很奇怪:

_backingStore = [[NSString stringWithString:string] copy];

那应该只是:

_backingStore = [string copy]; 

您的代码技术上复制字符串两次。我在技术上说,因为 - 由于实施细节,_backingStore最终会指向string(假设stringNSString而不是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],您尝试访问不再存在的对象。