Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIViewController subclass can't assign instance variable

This is utterly baffling to me, making me think I've missed something essential. But I'm trying to create a view controller container to manage two panes (similar to a SplitViewController but with some crucial differences). I've barely begun constructing the class, so I've only got 3 iVars right now:

@implementation SplitViewController{
    BOOL _showLeftPane;
    UIViewController *_leftController;
    UIViewController *_rightController;
}

No matter what I do, I cannot assign a view controller to the _leftController iVar. Mostly, the _rightController iVar can assign. It get's weirder. If I switch the order of the iVars so _rightController appears before _leftController, I can't assign either one on the first try. However, if I try again (literally write the assignment twice) the _leftController will take. Consistently, when I use the console to request the the value of the first controller in listed in the iVar declarations immediately after assignment, the value I get is (UIViewController *) $5 = 0x00000001 [no Objective-C description available]. WTF?

Out of curiosity (and because I can...I've barely started writing the class) I changed the super from UIViewController to NSObject. When I do that, assignment works. And really, assignment is just about all this class does right now beside create a blank view on loadView.

What am I missing?

Update The rest of the code:

The entire .h file:

#import <UIKit/UIKit.h>

@interface SplitViewController : UIViewController

@property (strong) UIViewController *leftViewController;
@property (strong) UIViewController *rightViewController;
@end

The entire .m file (note, I've tried synthesizing the properties, same behavior except it's harder to debug from the console):

@implementation SplitViewController{
    BOOL _showLeftPane;
    UIViewController *_rightNavController;
    UIViewController *_leftNavController;
}
#pragma mark - Init Methods
- (id)init{
    if (self = [super initWithNibName:nil bundle:nil]){
        _showLeftPane = YES;
    }
    return self;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {

    }
    return self;
}

#pragma mark - Properties
- (void) setLeftViewController:(UIViewController *)leftViewController{
    _leftNavController = leftViewController;
}
- (UIViewController *) leftViewController{
    return _leftNavController;
}
- (void) setRightViewController:(UIViewController *)rightViewController{
    _rightNavController = rightViewController;
}
- (UIViewController *) rightViewController{
    return _rightNavController;
}

Originally, I had all the normal view controller overridden methods (loadView, viewDidLoad, etc...), but I've since commented them out until I could get this problem ironed out. Also, I'm using ARC.

Update 2 I've tried setting up a simple NSObject container class, thinking maybe it had to do with the iVars being of class UIViewController that posed the problem. But the same problem persists. So wrapping the UIViewController into another variable doesn't work. I've also tried changing the iVar type to 'id' but the problem persists.

I'd also like to note that I've cleaned the project and utilized the clang static analyzer to check for logic errors.

Update 3 All assignment in this class are 'off'...often (but not always) assigning the next iVar in the list. I put in a bunch of dummy id iVars and assigned them random objects. I then stepped through each assignment (which I put in the init) and tested the value before and after assignment:

 if (self = [super initWithNibName:nil bundle:nil]) {
        _showLeftPane = YES;
        _dummy1 = [[NSObject alloc] init];
        _dummy2 = [[UIView alloc] init];
        _dummy3 = [[NSNumber alloc] init];
        _dummy4 = [[NSString alloc] init];
        _dummy5 = [[NSValue alloc] init];
        _dummy6 = [[UIColor alloc] init];
        _dummy7 = [[NSCache alloc] init];
        _dummy8 = [[UIButton alloc] init];
        _dummy9 = [[UIFont alloc] init];
        _rightController = [[UIViewController alloc] init];
        _leftController = [[UIViewController alloc] init];
    }

This is what I got (not the values don't change after assignment but those that do have values are often from the previous assignment). :

(lldb) po _showLeftPane
(BOOL) $2 = '\0' <nil>
(lldb) po _showLeftPane
(BOOL) $4 = '\0' <nil>
(lldb) po _dummy1
(id) $6 = 0x00000001 [no Objective-C description available]
(lldb) po _dummy1
(id) $8 = 0x00000001 [no Objective-C description available]
(lldb) po _dummy2
(id) $9 = 0x0a0461e0 <NSObject: 0xa0461e0>
(lldb) po _dummy2
(id) $11 = 0x0a0461e0 <NSObject: 0xa0461e0>
(lldb) po _dummy3
(id) $12 = 0x0866f340 <UIView: 0x866f340; frame = (0 0; 0 0); layer = <CALayer: 0xa05a790>>
(lldb) po _dummy3
(id) $14 = 0x0866f340 <UIView: 0x866f340; frame = (0 0; 0 0); layer = <CALayer: 0xa05a790>>
(lldb) po _dummy4
(id) $15 = 0x00000000 <nil>
(lldb) po _dummy4
(id) $17 = 0x00000000 <nil>
(lldb) po _dummy5
(id) $18 = 0x017c5a7c <object returned empty description>
(lldb) po _dummy5
(id) $20 = 0x017c5a7c <object returned empty description>
(lldb) po _dummy6
(id) $21 = 0x00000000 <nil>
(lldb) po _dummy6
(id) $23 = 0x00000000 <nil>
(lldb) po _dummy7
(id) $24 = 0x0865d300 <UIPlaceholderColor: 0x865d300>
(lldb) po _dummy7
(id) $26 = 0x0865d300 <UIPlaceholderColor: 0x865d300>
(lldb) po _dummy8
(id) $27 = 0x0a230af0 <NSCache: 0xa230af0>
(lldb) po _dummy8
(id) $29 = 0x0a230af0 <NSCache: 0xa230af0>
(lldb) po _dummy9
(id) $30 = 0x08637350 <UIButton: 0x8637350; frame = (0 0; 0 0); opaque = NO; layer = <CALayer: 0x8484920>>
(lldb) po _dummy9
(id) $32 = 0x08637350 <UIButton: 0x8637350; frame = (0 0; 0 0); opaque = NO; layer = <CALayer: 0x8484920>>
(lldb) po _rightController
(UIViewController *) $33 = 0x00000000 <nil>
(lldb) po _rightController
(UIViewController *) $35 = 0x00000000 <nil>
(lldb) po _leftController
(UIViewController *) $36 = 0x0a065180 <UIViewController: 0xa065180>
(lldb) po _leftController
(UIViewController *) $38 = 0x0a065180 <UIViewController: 0xa065180>

So, uh...is this a bug? If so, how in the heck would I file it? I don't even know what's going on....

Update 4 I got it to work, but only on my device. I generally tend to implement major changes on the simulator first (easier/quicker to debug) and then test it for performance on the device. Plus, I prefer to have a working version on my device so I can test it in my spare time. So I installed it on my device and the class works fine. It seems to only be broken in the simulator. Any ideas on why this might be? I really don't want to have to test the rest of my program development on the device alone. That'll slow things down a bit.

like image 816
Aaron Hayman Avatar asked Dec 22 '22 01:12

Aaron Hayman


2 Answers

Ok, I figured out the problem. I'm using Xcode 4.3, which is supposed to allow you to use the lldb debugger with iOS (or so Apple says). My problem went away when I changed the debugger to GDB. Though now I have a useful debugger, I've realized that the assignments were actually working correctly. The debugger was just spitting back the incorrect data. Gotta love it.

My advice: Don't use lldb on iOS. Even though Apple says the lldb debugger can be used with iOS, it's still not stable enough to rely on. So yeah, there went 6 hours of my life....

I've filed a bug with Apple: 10945570

UPDATE

Apple seems to have fixed this in Xcode 4.3.1. At least, so far LLDB is working well for me.

UPDATE 2

No, nevermind, LLDB is still messed up in the simulator.

UPDATE 3

So far as I can tell, this issue is now resolved in 4.3.2. I've been using the LLDB debugger now for about a week and I haven't had any problems with it giving me garbage values.

like image 142
Aaron Hayman Avatar answered Jan 15 '23 12:01

Aaron Hayman


This code does not seem correct:

@interface SplitViewController : UIViewController

@property (strong) UIViewController *leftViewController;
@property (strong) UIViewController *rightViewController;
@end

....

@implementation SplitViewController{
  BOOL _showLeftPane;
  UIViewController *_rightNavController;
  UIViewController *_leftNavController;
}

Is this what you really have? Because I would have expected something like this in the .h:

@interface SplitViewController : UIViewController
@property BOOL showLeftPane;
@property (strong) UIViewController* leftViewController;
@property (strong) UIViewController* rightViewController;
@end

And in the .m

@implementation SplitViewController
@synthesize showLeftPane;
@synthesize leftViewController;
@synthesize rightViewController;
...
@end

Or if you wanted to use explicit backing vars

@interface SplitViewController : UIViewController
{
    BOOL _showLeftPane;
    UIViewController* _leftViewController;
    UIViewController* _rightViewController;
}

@property BOOL showLeftPane;
@property (strong) UIViewController* leftViewController;
@property (strong) UIViewController* rightViewController;
@end

....

@implementation SplitViewController
@synthesize showLeftPane = _showLeftPane;
@synthesize leftViewController = _leftViewController;
@synthesize rightViewController = _rightViewController;
...
@end

From the posted code it looks like you are mixing up concepts between the @interface and the @implementation

As per the your comment, if you want private vars then the .m file should be something like:

@interface SplitViewController()
{
  BOOL showLeftPane;
  UIViewController* leftViewController;
  UIViewController* rightViewController;
}
@end

....

@implementation SplitViewController
...
@end

Actually I'll probably take that back. Not sure it works for vars .. but it does for methods.

YOu can make vars private through the use of @private in the interface

edit

My mistake. The original code is correct, in that you can put iVars in the implementation file like that since XCode 4.2. As per this SO question instance-variables-declared-in-objc-implementation-file you need 4.2+ and LLVM selected.

So now I sound like I don't know what I am talking about!

like image 36
Peter M Avatar answered Jan 15 '23 13:01

Peter M