2

I am dismissing a modal view controller whom is the delegate of a UIPickerView.

When I dismiss the view using

    [self dismissViewControllerAnimated:YES completion:NULL];

My app crashes, but only when the UIPickerView is showing, and currently the first responder.

I found the the cause of the crash to be a Zombie, this method, [UIPicker _updateSelectedRows] is showing in my instruments as the issue.

I am able to fix the issue by setting the UIPicker delegate and dataSource to nil prior to dismissing the view controller.

I have never needed to do this before, is there something I'm missing?

Here is a bare bones of the presenting view, presented in a modal segue. When you dismiss this using the IBAction, it will cause the crash

@interface VTSecViewController () <UIPickerViewDataSource, UIPickerViewDelegate>

@property (strong, nonatomic) UIPickerView *catPicker;
@property (strong, nonatomic) NSArray *catItems;
@property (weak, nonatomic) IBOutlet UITextField *pickerTF;

@end

@implementation VTSecViewController

- (IBAction)dismpress:(id)sender
{
    [self dismissViewControllerAnimated:YES completion:NULL];
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.catItems = @[@"one", @"two"];
    self.catPicker = [[UIPickerView alloc] init];
    self.pickerTF.inputView = self.catPicker;
    self.catPicker.delegate = self;
    self.catPicker.dataSource = self;
    [self.pickerTF becomeFirstResponder];
    // Do any additional setup after loading the view.
}

#pragma mark PickerView DataSource

- (NSInteger)numberOfComponentsInPickerView: (UIPickerView *)pickerView
{
    return 1;
}

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
    return [self.catItems count];
}

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
    self.pickerTF.text = self.catItems[row];
}

- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
    return self.catItems[row];
}


@end

This post helps clear up a few things

Community
  • 1
  • 1
DogCoffee
  • 19,820
  • 10
  • 87
  • 120
  • How are you presenting UIPickerview?? in actionsheet or some other viewcontroller? – Toseef Khilji Apr 02 '14 at 08:43
  • as an inputView of a UITextField – DogCoffee Apr 02 '14 at 09:03
  • Than you should use `[ self.categoryTF resignFirstResponder];` for dismissing pickerview. – Toseef Khilji Apr 02 '14 at 09:11
  • Yeah, I am using [self.view endEditing:YES]; prior to dismissing, but still get crash 1 in 5, only way to ensure no crash was setting delegates to nil. – DogCoffee Apr 02 '14 at 09:13
  • Do you execute setUpCatPicker more than once? Just wondering if you should only set `self.catPicker` if its nil? (clutching at straws though) – Flexicoder Apr 02 '14 at 09:16
  • No just once, having TF delegate with normal keyboard has no issues, must be a picker thing. Strange that if I embed in a Nav Controller the crash doesn't happen. As per my answer, option 3 in this answer http://stackoverflow.com/questions/18737186/position-of-navigation-bar-for-modal-view-ios7/20967695#20967695 – DogCoffee Apr 02 '14 at 09:19

1 Answers1

3

delegate (in this case your view controller) is supposed to give the information to pickerview on things like titles / row count / whattodo when you tap etc.. when you dismiss the controller while picker is still on the view. pickerview does not know that your controller died & it will keep asking the delegate about those information, hence -> crash.

when you set the delegate & datasource to nil. it will let know the pickerview that your delegate does not exist anymore so you wont get the crash.

sidenote : go to the delegate & datasource property in documentation it shows

@protocol UIPickerViewDataSource, UIPickerViewDelegate;

NS_CLASS_AVAILABLE_IOS(2_0) @interface UIPickerView : UIView <NSCoding, UITableViewDataSource>

@property(nonatomic,assign) id<UIPickerViewDataSource> dataSource;                // default is nil. weak reference
@property(nonatomic,assign) id<UIPickerViewDelegate>   delegate;                  // default is nil. weak reference

you can see the properties are "assign" (weak) and not "retain" (strong). hence when you dismiss the delegate & datasource uipickerview does not make any impact on the reference count of the delegate & it deallocates. it would have been a different case if this had been retain / strong. you should probably check out memory management topic on apple documentation on how these work. https://developer.apple.com/library/mac/documentation/general/conceptual/devpedia-cocoacore/MemoryManagement.html. delegates are weak references almost all the time.

nsuinteger
  • 1,503
  • 12
  • 21
  • But that doesn't always happen. I have this problem in one project, I'm trying to isolate it and reproduce in an app created from scratch, to no avail. – zrslv May 14 '14 at 18:29
  • Furthermore, I cannot understand how a weak property can cause a zombie situation – it should safely nullify itself. – zrslv May 14 '14 at 18:34
  • i was trying to explain why delegates/datasource must be kept weak/assign properties and not strong properties. I think you misunderstood. – nsuinteger May 15 '14 at 04:12
  • My bad then. Actually, I think the real cause of trouble is those properties are `assign` (i.e. unsafe_unretained), not `weak`. Combined with the fact that iOS often keeps holding +1 references to subviews of a deallocated view controller's view (for whatever reason) – ... – zrslv May 15 '14 at 11:36
  • I think you should better read a little more about uiviewcontroller & uiview relationship. UIViewController has a strong property UIView (so controller cannot survive without a view), where on the otherhand a UIView has only a weak/ delegate reference to UiViewController. (meaning uiview can survive well without a controller class). When ever you add a view-A to another view-B, that view-B's controller/class would +1 the view-A's reference count but this will not affect controllers reference count. thats why originally you can see the pickerview in UI, but the controller/delegate is dead. – nsuinteger May 15 '14 at 18:12
  • I believe most of the statements in your last comment doesn't make much sense, but this is a wrong place to discuss that. And thank you for trying to help. – zrslv May 15 '14 at 21:46