1

I'm fairly new to the iOS platform and I'm having some issues with the memory management. I'm passing an object (a Trial) in through an initializer of a custom UIViewController class and when the UIViewController finally receives it, the object is nil. I was hoping someone could point me in the right direction. I've included some of the source code below.

Trial.h

@interface Trial : NSObject {

    NSString *IRBNumber;
    NSString *PI;
    NSString *Sponsor;
    NSString *ContactName;
    NSString *ContactPhone;
    NSString *ContactEmail;
    NSString *Location;
    NSString *Objective;
    NSString *Eligibility;
    NSString *Name;
    NSString *DiseaseGroup;
    NSString *Age;
}

@property (retain, nonatomic) NSString *IRBNumber;
@property (retain, nonatomic) NSString *PI;
@property (retain, nonatomic) NSString *Sponsor;
@property (retain, nonatomic) NSString *ContactName;
@property (retain, nonatomic) NSString *ContactEmail;
@property (retain, nonatomic) NSString *ContactPhone;
@property (retain, nonatomic) NSString *Location;
@property (retain, nonatomic) NSString *Objective;
@property (retain, nonatomic) NSString *Eligibility;
@property (retain, nonatomic) NSString *Name;
@property (retain, nonatomic) NSString *DiseaseGroup;
@property (retain, nonatomic) NSString *Age;

@end

DiseaseControllersViewController.m

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

    Trial *trial = (Trial *)[dataArray objectAtIndex:indexPath.row];
    TrialDetailController *detailViewController = [[TrialDetailController alloc] initWithNibNameAndTrial:@"TrialDetailController" bundle:nil trial:trial];

    [self.navigationController pushViewController:detailViewController animated:YES];

    [detailViewController release];
    [trial release];
}

Here's the definition of the initializer

-(id)initWithNibNameAndTrial: (NSString*)NibNameOrNil bundle:(NSBundle*)nibBundlerOrNil trial:(Trial *)inTrial {
    self = [super initWithNibName:NibNameOrNil bundle:nibBundlerOrNil];
    if(self) {
        self.trial = inTrial;
    }
    return self;
}

TrialDetailController.h

#import "Trial.h"

@interface TrialDetailController : UITabBarController {

    Trial *trial;

}

@property (nonatomic, retain) Trial *trial;

-(id)initWithNibNameAndTrial: (NSString*)NibNameOrNil bundle:(NSBundle*)nibBundlerOrNil trial:(Trial *)inTrial;
-(IBAction)objectiveTabItemClick:(id)sender;
-(IBAction)detailsTabItemClick:(id)sender;

@end
dustyhoppe
  • 1,783
  • 16
  • 20
  • could you post the code of the TrialDetailController's init-Method? – samsam Dec 09 '11 at 15:48
  • You got it. I just posted it. – dustyhoppe Dec 09 '11 at 15:49
  • Have you tried debugging this? You can set breakpoints and view variable / property values in the gdb debugger (console window). See [Objective-C Debugging Tips in XCode4?](http://stackoverflow.com/a/7650979/590956). Just wondering what `po dataArray` would show in debugger. Where do you add values into this array, and do you initialize the array before you try to add objects into it? – Sam Dec 09 '11 at 15:50
  • I have tried setting breakpoints and debugging. In the initializer call, there are values set for the trial object and it clearly isn't nil. Once I step into the initializer, the inTrial parameter is nil. – dustyhoppe Dec 09 '11 at 15:53
  • 2
    Not sure why `inTrial` argument would be nil in `initWithNibNameAndTrial:bundle:trial:` if `trial` is a valid object (as seen by `po trial` in debugger). However, you are calling `[trial release]` at end of method which you shouldn't. The `[detailViewController release]` is fine since you `alloc`'ed it in that function, but trial did not increase its retainCount. – Sam Dec 09 '11 at 15:59
  • @Sam, ok cool. I removed the [trial release]. I'm still having the same problem, but I suppose I'm getting closer. – dustyhoppe Dec 09 '11 at 16:04

2 Answers2

0

You should not use self.trial within the init-method. Make that trial = [inTrial retain];.

Then you should not import Trial.h in TrialDetailController.h, do that in TrialDetailController.m. Put a @class Trial; (a forward definition) above the @interface line in TrialDetailController.h.

ott--
  • 5,642
  • 4
  • 24
  • 27
  • Why not using self.trial in init? If you have a retain property, you should never use retain/release ouside its getter or setter. That's why retain properties are there! – Sulthan Dec 09 '11 at 20:07
  • See http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html paragraph `Don’t Use Accessor Methods in Initializer Methods and dealloc`. – ott-- Dec 09 '11 at 20:23
  • Init and dealloc methods behave exactly the same as any other method. The reason why not to call them in init/dealloc usually comes of the fact that your setters don't behave correctly when they reference another property which has not been allocated yet or it is already deallocated. But note that the same problem can appear when you are not using the setter. It all depends on the contents of your setter and using retain/release won't make it better. Anyway you have to think about the order in which you release the properties. – Sulthan Dec 09 '11 at 20:45
  • +1 for supporting argument with documentation. I've always considered it best practice to not use property accessors in init. Cool to see a doc on it. However, I believe in most instances this isn't an issue and more than likely not what is causing the user grief. – Sam Dec 09 '11 at 21:01
0

My guess is that when your code does Trial *trial = (Trial *)[dataArray objectAtIndex:indexPath.row];

the variable dataArray is nil. Then trial would be nil, too.

The [trial release]; shouldn't be there as 从善如流 is suggesting.

I cannot find any other problem.

Only you don't have to declare the property variable: Trial *trial in your interface. A variable is created automatically when synthesizing (I expect you have @synthesize trial; somewhere).

Sulthan
  • 128,090
  • 22
  • 218
  • 270