insert subview to a UIView

Discussion in 'iPhone/iPad Programming' started by naphatkrit, Jul 28, 2011.

  1. naphatkrit, Jul 28, 2011
    Last edited by a moderator: Jul 29, 2011

    macrumors member

    Joined:
    Jul 18, 2011
    #1
    Hi,
    I'm writing an iPad app for my school, and I would like to make a custom view controller that controls the navigation of the user. I made a SwitchViewController class and added it to self.window in the app delegate. Then, in the Interface Builder, I added a view controller. I then added a view with an UIImage as the background. I then added a smaller view (583 x 964). I made an IBOutlet for that UIView, named it mainView. In the SwitchViewController file, I added these lines to viewDidLoad:

    Code:
    Grades_iPhone *tempGrades = [[Grades_iPhone alloc] initWithNibName:@"Grades_iPad" bundle:nil];
        grades = tempGrades;
        [tempGrades release];
        [self.mainView insertSubview:grades.view atIndex:0];
    When I tried to run it, Xcode gave me "EXC_BAD_ACCESS" on the line [self.mainView insertSubview:grades.view atIndex:0]; . However, when I use one of apple's provided template for navigation (for example, tabBarController), there is no problem. Please help me out!
     
  2. macrumors regular

    Joined:
    Oct 18, 2007
    Location:
    Chicago-area
    #2
    (Use CODE tags on your code)

    EXC_BAD_ACCESS means something is an invalid pointer (nil, already released, or otherwise bogus).

    The code snippet you posted begs a bunch of questions:
    a) What is grades and where is it defined?
    b) What is mainView and where is it defined?
    c) What are the values of self.mainView and grades when the exception is raised?

    The last answer is best determined by using the debugger and setting a breakpoint at the line of code that is going to fail.
     
  3. macrumors 6502

    Joined:
    Jan 13, 2011
    Location:
    Los Angeles, CA
    #3
    EXEC_BAD_ACCESS has to do with memory. It's hard to tell what's going on without seeing more of your code.
     
  4. thread starter macrumors member

    Joined:
    Jul 18, 2011
    #4
    I defined grades in the SwitchViewController header file from the class Grades_iPhone.

    my header file has the following codes:

    Code:
    @class Grades_iPhone;
    
    @interface SwitchViewController : UIViewController {
        UIView *mainView;
        Grades_iPhone *grades;
    }
    @property (nonatomic, retain) IBOutlet UIView *mainView;
    @property (nonatomic, retain) IBOutlet Grades_iPhone *grades;
    For some reason, just now when I tried another method, by making another view controller in the Interface Builder and pointing that view controller to the Grades_iPhone class and the appropriate xib file (that way, I don't have to initialize grades), it works now. my current implementation file is like this:

    Code:
    #import "SwitchViewController.h"
    #import "Grades_iPhone.h"
    
    - (void)viewDidLoad
    {
        [self.mainView insertSubview:grades.view atIndex:0];
        [super viewDidLoad];
    }
    I'm not sure what's the difference between this method and my previous one, but it seems to solve the problem. I hope this helps anyone else with similar problem.
     
  5. macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
    #5
    I don't know if this is root-cause of your problem, but the line above in red is problematic. It's bypassing the property and assigning directly to the ivar. This means the new Grades_iPhone object is not being retained, the send of release on the next line is deallocating the object, and therefore grades now points to a dead object.

    The line should have been:
    Code:
    [COLOR=green]self.[/COLOR]grades = tempGrades;
    It's to avoid this very confusion that I either don't declare explicit ivars for my properties, or I name my ivars differently to my properties.
     
  6. Sykte, Jul 29, 2011
    Last edited by a moderator: Jul 30, 2011

    macrumors regular

    Joined:
    Aug 26, 2010
    #6
    How did you come to the conclusion this was improper?


    Let's break this down.

    Line 1.
    You first create an object called tempGrades, with a retain count of 1.

    Line 2.
    You assign the pointer of tempGrades to grades, retain count still 1.

    Line 3.
    You then release tempGrades dropping the retain count to 0.

    Line 4.
    You then try to add grades as a subView.


    What happens when a retain count drops to 0?
     
  7. macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
    #7

    I'm confused by your post(s). Are you asking a genuine question? Being rhetorical? Perhaps asking the question of the OP?
     
  8. macrumors regular

    Joined:
    Aug 26, 2010
    #8

    No I was asking you how you came to the conclusion what he did was improper?

    I originally had the post separated for this exact reason however a demi god joined them together. Oops guess consecutive posting is against forums rules.
     
  9. macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
    #9
    Because it is like you said. The NSView is being over-released and a dead NSView object is being added to the view hierarchy. NSView is going to retain its subviews, so worse, not only is a dead object being used, it's also being retained and being resurrected.

    What happens when the retain count goes to zero is that the runtime will send a dealloc message to the object. What happens when you use or resurrect a dead object, depends on how teared down the object is by the end of dealloc, and that depends on the class. But anyone coding to use or resurrect a dead object is asking for a massive amount hurt and for spending many hours in the debugger trying to diagnose suitable crashes.

    I assumed the OP had intended to set the grades ivar via the retained grades property, because then the code makes sense. But had either forgotten the self. before grades to make that happen, or had mistakenly believed the code was setting via the property.
     
  10. macrumors regular

    Joined:
    Aug 26, 2010
    #10

    Not sure what you mean by resurrected however bypassing the property isn't necessarily improper.


    Original code:
    Code:
    Grades_iPhone *tempGrades = [[Grades_iPhone alloc] initWithNibName:@"Grades_iPad" bundle:nil];
        grades = tempGrades;
        [tempGrades release];
        [self.mainView insertSubview:grades.view atIndex:0];
    
    The original code can be rewritten as follows.

    Code:
    grades = [[Grades_iPhone alloc] initWithNibName:@"Grades_iPad" bundle:nil];
    
    [self.mainView insertSubview:grades.view atIndex:0];
    
    or, which is probaly better for beginners.

    Code:
    self.grades = [[[Grades_iPhone alloc] initWithNibName:@"Grades_iPad" bundle:nil] autorelease];
    
    [self.mainView insertSubview:grades.view atIndex:0];
    
    Are you saying he should write the code this way?

    Code:
    self.grades = [[Grades_iPhone alloc] initWithNibName:@"Grades_iPad" bundle:nil];
    
    [self.grades release];
    
    [self.mainView insertSubview:grades.view atIndex:0];
    

    Bypassing a property to access it's own member variable in the proper scope can have it's benefits.
     
  11. Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #11
    Such as?
     
  12. jiminaus, Jul 30, 2011
    Last edited: Jul 30, 2011

    macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
    #12
    What I mean by resurrecting is retaining an object after it's retain count has reached zero and dealloc has been called. This results in a zombie object, an object that should be dead but isn't.


    This leaks memory. I'll leave it to you to figure out why.

    I thought grades was being added directly, not grades.view. So this is okay, assuming the OP releases grades in dealloc.


    Acceptable but I don't like the use of the property and the use of the ivar within the same method. I find it to be sloppy style.


    Absolutely not. Releasing via a retain property like this is horrid!

    What I suggest, and continue to suggest, is just to add the initial self. that's required to the original code.



    I second Dejo. Please expand on this.
     
  13. admanimal, Jul 30, 2011
    Last edited: Jul 30, 2011

    macrumors 68040

    Joined:
    Apr 22, 2005
    #13
    Using the ivar directly requires less code to initialize it.

    Suppose you have this property:

    Code:
    @property (nonatomic, retain) Grades *grades;
    
    and you want to initialize it. The two most reasonable ways to do it are:

    Code:
    grades = [[Grades alloc] initWithStuff:...];
    
    or

    Code:
    Grades *tempGrades = [[Grades alloc] initWithStuff:...];
    self.grades = tempGrades;
    [tempGrades release];
    
    I prefer the first way of doing it.
     
  14. macrumors 603

    Joined:
    Aug 9, 2009
    #14
    I see a contradiction here.

    First, you're not initializing it from another class. The use of self.grades means you're initializing it from the class that defines the ivar, or one of its subclasses, otherwise self won't work in this code. The additional fact of the unadorned ivar name, also means you're not initializing it from another class, but from the class (or subclass) that defines the ivar.

    Second, I don't see why you'd be initializing an ivar from another class. If there are ownership concerns (such as use of -retain or -copy) that are addressed by the property's attributes, then the property should be used. That might even be a logical reason for making it a property. If it wasn't intended to be a property, then it would remain just an ivar, and perhaps be restricted in scope with @protected or @private. In any case, the initial value should be the responsibility of the class whose ivar it is, not some external class. And any non-initial assignment of a value by some other class should use the prescribed public API, which is presumably the property.

    Third, the code was for a class that defined the ivar and the property, not an external class. I don't see why the rules for self-access would be the same as the rules for other-access. If they were, then encapsulation has no purpose.
     
  15. macrumors 68040

    Joined:
    Apr 22, 2005
    #15
    Sorry, I'm not sure why I said "from some other class." Obviously everything I was doing is supposed to be in the interface/implementation of one class.
     
  16. macrumors regular

    Joined:
    Aug 26, 2010
    #16
    Sorry Jiminaus, I meant this.
    Code:
    grades = tempGrades;
    However quoted this.

    Code:
    self.grades = tempGrades;

    I should have been more specific instead of making a blanket statement as I did. Let’s say your application is truly constrained by device resources. Within your application you create a million expensive objects and those objects create other objects. The parent objects then set the default state of its child objects during initialization. Something like this..
    Code:
    self.var = [[[myobject alloc] init] autorelease];
    
    will result in millions and millions of release and autorelease messages being sent. So in a tightly controlled environment you can save precious resources by bypassing the properties and doing something like this.

    Code:
    _var = [[myobject alloc] init];
    
    Let me state I would only bypass the property during object creation and only bypass the locally scoped properties. Setting anything beyond local would be nonsense.
     
  17. macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
    #17
    I agree, with one proviso (is that the word?). That is, that the program fully works albeit slowly, and that profiling shows this to be the bottleneck. Otherwise, it's case of premature optimisation.
     

Share This Page