Objective C NSString *属性保留计数奇数

时间:2008-12-31 14:45:57

标签: ios objective-c xcode cocoa cocoa-touch

我有以下示例类:

Test.h:

@interface Test : UIButton {
    NSString *value;
}
- (id)initWithValue:(NSString *)newValue;
@property(copy) NSString *value;

Test.m:

@implementation Test
@synthesize value;
- (id)initWithValue:(NSString *)newValue {
    [super init];   
    NSLog(@"before nil value has retain count of %d", [value retainCount]);
    value = nil;
    NSLog(@"on nil value has retain count of %d", [value retainCount]);
    value = newValue;
    NSLog(@"after init value has retain count of %d", [value retainCount]);
    return self;
}

产生以下输出:

2008-12-31 09:31:41.755 Concentration[18604:20b] before nil value has retain count of 0
2008-12-31 09:31:41.756 Concentration[18604:20b] on nil value has retain count of 0
2008-12-31 09:31:41.757 Concentration[18604:20b] after init value has retain count of 2147483647

我称之为:

Test *test = [[Test alloc] initWithValue:@"some text"];

值不应该有1的保留计数吗?我错过了什么?

感谢您的帮助。

9 个答案:

答案 0 :(得分:21)

不要看保留计数。它们没用,只会误导你 - 你无法确定没有其他东西保留一个对象,你从某个地方获得的对象不会被共享。

相反,请专注于对象所有权,并按照Cocoa memory management rules的字母进行操作。这样,无论Cocoa在幕后为您做什么优化,您的内存管理都是正确的。 (例如,对于不可变对象,仅将-copy实现为-retain。)

此外,了解对象的属性与对象中的实例变量之间的差异严重。在您的问题代码中,您要为实例变量赋值。那个实例变量就是:一个变量。分配给它将表现得像任何其他变量赋值。要使用该属性,必须使用点语法或括号语法来实际调用属性的setter方法:

self.value = newValue;     // this is exactly equivalent to the next line
[self setValue:newValue];  // this is exactly equivalent to the previous line

为点语法和括号语法生成的代码是相同的,并且都不会直接访问实例变量。

答案 1 :(得分:11)

您正在传入一个文字字符串。编译器可能会在静态内存中分配它,并将保留计数设置为最大可能值。

尝试使用动态分配的字符串,看看会发生什么。

NSString* string = [[NSString alloc] initWithString: @"some text"];
Test* test = [[Test alloc] initWithValue: string];

答案 2 :(得分:3)

你传入的是一个字符串常量,它实际上无法解除分配。我认为2147483647可能是UINT_MAX,这基本上意味着该对象无法释放。

答案 3 :(得分:3)

我想你想这样做:

self.value = newValue;

将调用属性setter并导致副本发生。 “value = newValue”只是将指针值赋给实例变量。

答案 4 :(得分:3)

你有一个不可变字符串的引用。赋值不需要复制值(字符串数据),因为它是不可变的。如果你执行一个可变操作,比如value = [newValue uppercaseString],那么它应该将这些位复制到值中,并且值的保留计数递增。

答案 5 :(得分:2)

您不应该关注保留计数,只需遵循Cocoa内存管理规则。 http://iamleeg.blogspot.com/2008/12/cocoa-memory-management.html

答案 6 :(得分:0)

嗯..我们越来越近了。

似乎newValue的保留计数也是2147483647。

我尝试使用相同的保留计数结果动态分配字符串。

我在这里找到了一篇有用的文章:http://www.cocoadev.com/index.pl?NSString

FTA:

@“”返回的NSString是否需要释放,还是自动释放? 都不是。 @“” - 字符串是类NSConstantString ?,因此就像lisp中的原子一样;他们到处闲逛。也就是说,如果在代码中的两个不同位置使用@“cow”,它们将引用同一个对象。 我不认为-release或-autorelease对其中任何一个做任何事情。

如果我对该属性进行“复制”,是否应该将目标内存的内容复制到保留计数为1的新内存中?在这种情况下,复制属性似乎什么都不做?

答案 7 :(得分:0)

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    char *cstr = "this is a c string";

    NSString *str = [[NSString alloc] initWithUTF8String:cstr];
    NSLog(@"rc1: %d", [str retainCount]);

    [pool drain];
    return 0;
}

如果运行上面的代码,它将显示保留计数为1

答案 8 :(得分:0)

在Cocoa中,当您在同一区域内请求副本时,许多不可变对象将自行保留。如果保证对象不会改变(即它的不可变性),那么精确复制是多余的。

在Objective-C中,常量字符串类与Cocoa的NSString类是分开的,尽管它可能是NSString的子类(我不太确定)。这个常量字符串类可以覆盖NSObjectretainrelease之类的dealloc方法,这样它们什么都不做,并且还覆盖retainCount以便始终返回相同的数字UINT_MAX左右。这是因为在静态内存中创建了Objective-C常量字符串。它必须具有Cocoa对象的整体一般行为(当使用Cocoa时),以便它可以添加到数组,用作字典等的键,除了它的内存管理,因为它的分配方式不同。

免责声明:我实际上并不知道我在说什么。