1

私は最終日を動的ストレージの問題の追跡に費やしましたが、トレイルの終わりには、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)
4

2 に答える 2

6

この回答は問題に直接対処するものではありませんが、間接的に問題を修正し、より保守しやすいデザイン パターンに導きます。

NSString をサブクラス化しないでください。

代わりに、コンポジションを使用してください。

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

実際のクラッシュは次のとおりです。

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

そのメソッドは自動解放されたオブジェクトを返し、プールが空になると割り当てが解除され、ダングリング参照が残ります。

retainCount他の人が述べたように、まったく役に立たない.

これは奇妙であることに注意してください。

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

それは単に次のようにする必要があります。

_backingStore = [string copy]; 

Your code is technically copying the string twice. I say technically, because -- due to an implementation detail -- _backingStore will end up pointing to string (assuming string is an NSString and not an NSMutableString.

I was very much an insider back in the NeXT days, but have been mostly away or only used Objc base.

Aha! So was I, having started ObjC programming in 1989.

That'd explain where you are coming from a bit!

Instead of retainCount, an issue like this is quite easy to debug using zombies. You can turn it on in the options pane of the scheme in Xcode.

The "whentouseretaincount.com" site links to an article I wrote about retain count. You might find it interesting in that it also illuminates some details of memory management, in general.

Linux GnuStep の世界への Apple ドキュメント

これは、質問で注意することも重要です。GNUStep はほとんど Apple のものと同じですが、OpenStep の世界に少し近づいています。GNUStep にゾンビ検出機能があるかどうかは思い出せませんが、あると思います。Linux には、他にも非常に強力なメモリ デバッグ ツールがあります。

retainCountまだ脆弱性が蔓延していますが、シングルスレッドのコマンドラインツールを扱う場合は、やや安定しています。ただし、自動リリースされたものには注意する必要があります。

于 2013-07-14T19:00:46.113 に答える
2

への呼び出しは、自動解放されcomponentsSeparatedByString:た を返します。NSArrayつまり、保持カウントは 1 ですが、自動解放プールが空になるとすぐにカウントが減少します。これを IDStringBug の deallocの呼び出しと組み合わせるとrelease、配列が何度も解放されていることがわかります。

実際、IDStringBug の dealloc が呼び出されるまでに、パス配列は既に割り当て解除されています。そのため、保持カウントを決定しようとすると (呼び出しで、[path retainCount]存在しなくなったオブジェクトにアクセスしようとしています。

于 2013-07-14T17:17:43.010 に答える