开发者

Apparently I don't know how to write a setter

I have a nice object that describes a relatively large data set. I decided to implement some helper functionality in the object.

Basically, instead of using the standard setter for a NSString, I define my own setter and set another object at the same time.

For example:

-(void) setNumber:(NSString *)number_in
{
    number = [number_in copy];
    title = @"Invoice ";
    title = [title stringByAppendingString:number];
}

I know I will need "title" as a property in a certain format. Title is based on the number, so I created a setter to set number and title in one punch. (title has the default synthesized setter...I don't define it elsewhere)

For some reason, I'm getting the message sent to deallocated instance error. If I delete this setter, the code works fine.

My property definition is here:

@property (nonatomic, copy) NSString *number;
@property (nonatomic, copy) NSString *title;

I've tried retaining, to no avail. I setup malloc stack logging and logged this:

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

In the end, I keep getting this error:

-[CFString 开发者_运维知识库release]: message sent to deallocated instance 0x4b5aee0

Thanks a million in advance :)


Use self.title to invoke your synthesized setter as well as release the old value for number.

- (void)setNumber:(NSString *)number_in
{
    [number release];
    number = [number_in copy];
    self.title = [NSString stringWithFormat:@"Invoice %@", number];
}


title is getting autoreleased and number is leaking in the program. To write a thorough setter, you should first copy the passed in object, and then do a release.

-(void) setNumber:(NSString *)number_in {
    if (number == number_in) {
        return;
    }

    NSString *oldValue = number;
    number = [number_in copy];
    [oldValue release];

    self.title = [title stringByAppendingString:number];
}

The reason for copying first and then releasing is because calling copy on an immutable object may return the same object back instead of creating a new copy. So if setNumber is called twice with the same object, and number was released first, it becomes invalid, then calling copy on that invalid object next could cause issues.

The if check is an optimization step, which you could remove if you wanted.

Also, you may want to checkout this article on writing custom setters.

As @tia and @Mark have posted, if title is always dependent on the value of number, then title should be a readonly property. The modified setNumber may then look like,

- (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];
}

Additional checks may be needed for when a nil number_in is passed, as when a nil is passed to stringByAppendingString, a NSInvalidArgumentException is raised. So here goes the final version of this setter with that check,

- (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];
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜