显然我不知道如何写一个二传手

时间:2011-02-19 17:11:37

标签: iphone objective-c cocoa-touch setter memory-management

我有一个很好的对象来描述一个相对较大的数据集。我决定在对象中实现一些辅助功能。

基本上,我不是使用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

提前感谢一百万人:)

2 个答案:

答案 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];
    }
}