我有一个很好的对象来描述一个相对较大的数据集。我决定在对象中实现一些辅助功能。
基本上,我不是使用NSString的标准setter,而是定义自己的setter并同时设置另一个对象。
例如:
-(void) setNumber:(NSString *)number_in
{
number = [number_in copy];
title = @"Invoice ";
title = [title stringByAppendingString:number];
}
我知道我需要“title”作为某种格式的财产。标题是基于数字的,所以我创建了一个setter来一键设置数字和标题。 (标题有默认的合成setter ......我没有在其他地方定义它)
出于某种原因,我将消息发送到解除分配的实例错误。如果我删除这个setter,代码工作正常。
我的属性定义在这里:
@property (nonatomic, copy) NSString *number;
@property (nonatomic, copy) NSString *title;
我试过保留,但没有用。我设置了malloc堆栈日志并记录下来:
Alloc: Block address: 0x06054520 length: 32
Stack - pthread: 0xa003f540 number of frames: 30
0: 0x903ba1dc in malloc_zone_malloc
1: 0x102b80d in _CFRuntimeCreateInstance
2: 0x102d745 in __CFStringCreateImmutableFunnel3
3: 0x10824dd in _CFStringCreateWithBytesNoCopy
4: 0xae222e in -[NSPlaceholderString initWithCStringNoCopy:length:freeWhenDone:]
5: 0xaf9e8e in _NSNewStringByAppendingStrings
6: 0xaf9a76 in -[NSString stringByAppendingString:]
7: 0x112ba in -[Invoice setNumber:] at Invoice.m:25
8: 0x11901 in -[Invoice copyWithZone:] at Invoice.m:47
9: 0x107c7ca in -[NSObject(NSObject) copy]
10: 0x1117632 in -[NSArray initWithArray:range:copyItems:]
11: 0x107f833 in -[NSArray initWithArray:copyItems:]
12: 0x5595 in -[InvoicesTableViewController wrapper:didRetrieveData:] at InvoicesTableViewController.m:96
13: 0x4037 in -[Wrapper connectionDidFinishLoading:] at Wrapper.m:288
14: 0xb17172 in -[NSURLConnection(NSURLConnectionReallyInternal) sendDidFinishLoading]
15: 0xb170cb in _NSURLConnectionDidFinishLoading
16: 0x18ca606 in _ZN19URLConnectionClient23_clientDidFinishLoadingEPNS_26ClientConnectionEventQueueE
17: 0x1995821 in _ZN19URLConnectionClient26ClientConnectionEventQueue33processAllEventsAndConsumePayloadEP20XConnectionEventInfoI12XClientEvent18XClientEventParamsEl
18: 0x18c0e3c in _ZN19URLConnectionClient13processEventsEv
19: 0x18c0cb7 in _ZN17MultiplexerSource7performEv
20: 0x10fd01f in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
21: 0x105b28b in __CFRunLoopDoSources0
22: 0x105a786 in __CFRunLoopRun
23: 0x105a240 in CFRunLoopRunSpecific
24: 0x105a161 in CFRunLoopRunInMode
25: 0x1c29268 in GSEventRunModal
26: 0x1c2932d in GSEventRun
27: 0x39542e in UIApplicationMain
28: 0x2199 in main at main.m:14
29: 0x2115 in start
最后,我不断收到此错误:
-[CFString release]: message sent to deallocated instance 0x4b5aee0
提前感谢一百万人:)
答案 0 :(得分:7)
使用self.title
调用合成的setter并释放number
的旧值。
- (void)setNumber:(NSString *)number_in
{
[number release];
number = [number_in copy];
self.title = [NSString stringWithFormat:@"Invoice %@", number];
}
答案 1 :(得分:5)
title
正在自动释放,number
正在泄漏。要编写一个完整的setter,首先应复制传入的对象,然后执行发布。
-(void) setNumber:(NSString *)number_in {
if (number == number_in) {
return;
}
NSString *oldValue = number;
number = [number_in copy];
[oldValue release];
self.title = [title stringByAppendingString:number];
}
首先复制然后释放的原因是因为在不可变对象上调用copy
可能会返回相同的对象而不是创建新副本。因此,如果setNumber
使用相同的对象调用两次,并且number
首先被释放,则它将变为无效,然后在该无效对象上调用copy
可能会导致问题。
if
检查是一个优化步骤,您可以根据需要删除。
此外,您可能希望在编写自定义设置器时签出此article。
正如@tia和@Mark发布的那样,如果title始终依赖于number的值,那么title应该是readonly
属性。修改后的setNumber
可能看起来像,
- (void) setNumber:(NSString *)number_in {
if (number == number_in) {
return;
}
NSString *oldNumber = number;
number = [number_in copy];
[oldNumber release];
NSString *oldTitle = title;
title = [title stringByAppendingString:number];
[oldTitle release];
}
当nil
number_in被传递时,可能需要进行额外检查,例如当nil
传递给stringByAppendingString
时,会引发NSInvalidArgumentException
。所以这里有一个检查的最终版本,
- (void) setNumber:(NSString *)number_in {
if (number == number_in) {
return;
}
NSString *oldNumber = number;
number = [number_in copy];
[oldNumber release];
if (number) {
NSString *oldTitle = title;
title = [title stringByAppendingString:number];
[oldTitle release];
}
}