[英]Apparently I don't know how to write a setter
我有一个很好的对象,描述了一个相对较大的数据集。 我决定在对象中实现一些辅助功能。
基本上,我不是使用NSString的标准setter,而是定义自己的setter并同时设置另一个对象。
例如:
-(void) setNumber:(NSString *)number_in
{
number = [number_in copy];
title = @"Invoice ";
title = [title stringByAppendingString:number];
}
我知道我需要“标题”作为某种格式的财产。 标题是基于数字的,所以我创建了一个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
提前一百万感谢:)
使用self.title
调用合成的setter以及释放number
的旧值。
- (void)setNumber:(NSString *)number_in
{
[number release];
number = [number_in copy];
self.title = [NSString stringWithFormat:@"Invoice %@", number];
}
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
检查是一个优化步骤,如果需要,可以删除。
另外,您可能想要查看有关编写自定义setter的文章 。
正如@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];
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.