Is this really your code? There are some oddities that make me wonder. Also, your subject line talks about NSZombie (an instance of a class that gets substituted into an object when it is released, if you have NSZombieEnabled set), but your complaint is that a property is nil when you don't expect it. That has nothing to do with zombies.
Let me annotate your example, as I think the problem might be clearer if the code were cleaner: On 7 Aug 2011, at 8:10 AM, Scott Steinman wrote: > -(void)setUp; > -(void)start; > -(void)changeWords:(NSTimer*)theTimer; > -(NSArray *)wordsInPhrase:(NSString *)thePhrase; > @property (nonatomic, assign) int numWords; > @property (nonatomic, assign) NSUInt wordChangeInterval; Here's my first reason to believe you haven't provided your real code. There is no such thing as an NSUInt. I understand your desire to provide a compact example, but we can't help you if it hides your problem. And, getting the example to compile and run may point you to your bug. > @property (nonatomic, copy) NSString *phrase; > @property (nonatomic, copy) NSArray *words; Properties: Tell us whether you've backed them explicitly with instance variables, or allowed @synthesize to generate instance variables; whether the backing ivars have the same name as the properties; and whether you're providing getters or setters for any of them. Given that we're not seeing your actual code, I don't know whether you're assigning nil to ->words or .words somewhere. Try overriding -(void) setWords: (NSArray *) newWords { if (newWords != words) { [words release]; words = [newWords copy]; } } and setting a breakpoint at the beginning. Extra points for conditioning the breakpoint on newWords == nil. When the breakpoint hits, examine the backtrace. It won't catch your overuse of direct access to backing ivars (a major theme of this message), but it will be something. > @property (nonatomic, copy) NSTimer *wordChangeTimer; NSTimer doesn't implement <NSCopying>, as copying an NSTimer doesn't make sense. If you had actually used the property, the runtime would have caught this for you. > - (id)init > { > self = [super init]; > if (self) { > phrase = [[NSString stringWithString:@"This is the phrase to display"] > retain]; phrase = @"This is the phrase to display"; // stringWithString: is almost never what you intend. [@"This is the phrase to display" copy] is the prudent thing to do, but not idiomatic or necessary. > wordChangeInterval = 0.2; This was declared as "NSUInt", I assume is some sort of integer. The ivar will be set to zero. > } > return self; > } > > -(void)setUp > { > words = [[self wordsFromPhrase:phrase]] retain]; Your brackets are unbalanced. self.words = [self wordsFromPhrase: self.phrase]; 1. You've got a property. Use it. 2. You're simulating* the retain attribute, when your property declaration said you want copy. This suggests to me that you don't mean it. * ("Simulating" in that you're leaking the previous occupant of ->words. The synthesized accessor would have taken care of that.) > [self start]; Do you ever want to change .words without doing [self start]? If not, make the call to -start inside setWords:. Then self.words = [self wordsFromPhrase: self.phrase] can completely replace -setUp. But see: > } > > -(NSArray *)wordsInPhrase:(NSString *)thePhrase > { Perchance, do you call this method with any phrase other than self.phrase? If not, then you can implement -(NSArray *) words to return [self wordsFromPhrase: self.phrase], the property can become readonly, and your worry about self.words being nil goes away. > NSArray *wordArray; > > [wordArray arrayByAddingObjectsFromArray:[phrase > componentsSeparatedByString:@" "]]; This sentence no verb. wordArray has no initial value, so sending arrayByAddingObjectsFromArray: to it should crash most of the time (another reason I don't believe you're showing your code). arrayByAddingObjectsFromArray: returns an NSArray*, but you're throwing away the returned value, and the method itself has no side effects. And thePhrase isn't used anywhere in the method. > numWords = [wordArray count]; self.numWords = wordArray.count; // Use the property. > return wordArray; > } I _think_ you mean: - (NSArray *) wordsInPhrase: (NSString *) thePhrase { NSArray * wordArray = [thePhrase componentsSeparatedByString: @" "]; self.numWords = wordArray.count; return wordArray; } But I'm _guessing_ you mean: - (NSArray *) wordsInPhrase { NSArray * wordArray = [self.phrase componentsSeparatedByString: @" "]; self.numWords = wordArray.count; return wordArray; } and that you _really_ mean: @property(nonatomic, readonly) NSUInteger numWords; @property(nonatomic, readonly) NSArray *words; @property(nonatomic, retain) NSArray * backingWordsArray; ... @synthesize backingWordsArray; - (NSArray *) words { if (! self.backingWordsArray) self.backingWordsArray = [self.phrase componentsSeparatedByString: @" "]; return self.backingWordsArray; } - (NSUInteger) numWords { return self.words.count; } - (void) setPhrase: (NSString *) newPhrase { if (newPhrase != phrase) { [phrase release]; phrase = [newPhrase copy]; self.backingWordsArray = nil; [self start]; } } > - (void) start > { > currentWordIndex = 0; This won't compile from the code you're showing. Is it an ivar? > wordChangeTimer = [[NSTimer > scheduledTimerWithTimeInterval:wordChangeInterval [My customary objection to accessing an ivar directly.] > target:self > > selector:@selector(changeWords:) > userInfo:nil > repeats:YES] retain]; self.wordChangeTimer = [NSTimer ... repeats: YES]; If you let the property manage your memory, you won't have to do it yourself. And if you have a setter method, you can invalidate the old timer. > } > > - (void)changeWords:(NSTimer*)theTimer > { > currentWordIndex += 1; > if (currentWordIndex > numWords) > currentWordIndex = 0; > messageLayer.string = [self.words objectAtIndex:currentWordIndex]; > } For once you're actually using the property. If this is the only time, one has to wonder whether there is a -words method that doesn't do what you think it does. > Now, the strangeness: words exists and is OK in setUpDisplay and startDisplay > in that it contains the right words from the phrase. But in changeWords:, > somehow words is nil. I'm at a loss to figure out how words could be released > between start and changeWords:. I'd appreciate some help. What are setUpDisplay and startDisplay? Are they the same as setUp and start? A property going nil is not the same as releasing the object it once pointed to. The two leading candidates for the bug are: * There has been an assignment. At the very minimum, override setWords:, be religious about using .words as a property, and audit where the setter is called. * There is a -words getter, in the class or its parent, that does not reflect the backing ivar. Switching to near-exclusive access through the property (except in an init..., dealloc,** or the accessors themselves) may cure it; or using my suggestion of dropping most of the ivars and computing the properties on-demand; may cure the problem. — F ** (Opinions differ on whether inits or dealloc should use accessor methods or the ivars that implement them. I feel strongly about the latter. See the archives for the religious wars.) _______________________________________________ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com