我正在构建一个小型iphone应用程序并使用单例来存储和更新当用户点击屏幕上的字母或数字以形成代码时更新的字符串。
即。他们点击3然后S然后4我需要跟踪和组合输入给我“3S4”说。初始化单例时,它会创建一个空NSString,然后我使用stringByAppendString方法添加下一个被点击的字母/数字。当我第一次尝试这个时,我没有[enteredCode retain]行,并且应用程序会因EXC_BAD_ACCESS而崩溃,总是在2个输入之后。我设置了NSZombie属性,它告诉我,已经取消了已输入的编码,但我不知道发生的地点和方式。我所知道的是,在addInput方法结束时,它会将retainCount报告为2,然后在我看到之后直接(通过从其他地方调用单例)它会下降到1(当保留行在那里时) )。
我的问题是:虽然我通过添加[enteredCode retain]所做的事情对我有用,但我在这里打破了一些规则,或者以错误/错误的方式解决这个问题?我只是看不出为什么要释放字符串。
我是Objective-C btw
的新手在MySingleton.h中
@interface MySingleton : NSObject {
NSString *enteredCode;
}
在MySingleton.m
-(void) addInput:(NSString *) input
{
NSLog(@"enteredCode retain count is : %d \n ",[enteredCode retainCount]);
enteredCode = [enteredCode stringByAppendingString:input];
NSLog(@"enteredCode retain count is : %d \n ",[enteredCode retainCount]);
[enteredCode retain]; // without this the app crashes
NSLog(@"enteredCode retain count is : %d \n ",[enteredCode retainCount]);
}
-(id) init
{
self = [super init];
if (self)
{
enteredCode = @"";
}
return self;
}
答案 0 :(得分:3)
首先,永远不要使用-retainCount
方法。保留在对象上的绝对数量是框架的实现细节,并且通常会返回令人困惑的结果。
保留计数是你应该完全保持平衡的一组增量。如果您将保留计数添加到某个内容,则必须在某处release
或autorelease
该对象。故事结束。
掌握了这些知识,崩溃的来源是一个相当常见的内存管理错误。
enteredCode = [enteredCode stringByAppendingString:input];
每次执行该行代码时,您都将使用自动释放的NSString实例替换enteredCode
。自动释放池耗尽,下次使用enteredCode
时程序崩溃。
您保留enteredCode
的解决方案只是解决方案的一半。您还需要确保释放enteredCode
的原始值。请参阅内存管理文档。
如果这是我的应用程序,我会将enteredCode
转换为复制字符串的@property,并始终通过该属性设置和访问enteredCode
,从不在我的代码中手动保留或释放它(外部当然是-dealloc
。
答案 1 :(得分:1)
NSString的stringByAppendingString:
返回一个新的NSString,它通过将一个字符串附加到另一个字符串,并将新的NSString设置为autorelease,这会清空自动释放池,并且您的下一次运行会使应用程序崩溃。您正在使用stringByAppendingString:
重新定义现有字符串,这会导致保留问题。 (或者,使用NSMutableString,你可以避免这种情况。)
顺便说一句,您可以在if (self = [super init])
覆盖中执行init
。如果声明发生或可能发生,声明将返回true。
答案 2 :(得分:1)
以下是您的代码的外观:
@interface MySingleton : NSObject {
NSString *enteredCode;
}
@property (nonatomic, retain) NSString *enteredCode;
@end
@synthesize enteredCode;
-(void) addInput:(NSString *) input
{
self.enteredCode = [self.enteredCode stringByAppendingString:input];
}
- (void)dealloc {
[enteredCode release];
}
@end