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];
}
}
精彩评论