Title: Why does this init method when loading a nib return an object out of scope?
Tags: iphone;ios;uiview;bundle;nib
Question: Why does this init method return an object out of scope?

Using XCode 4.2, base SDK of 4.3, and ARC, I'm trying to load an UIView from a nib (not a UIViewController).  I need to not use a UIViewController at all in the process.

After reading this answer to an S.O. question here, it looks like it can be done:
How to load a UIView using a nib file created with Interface Builder
(The answer by user "MusiGenesis" describes the process)

I created a sub-class of UIView with a single label:

```@interface MyView : UIView
@property (unsafe_unretained, nonatomic) IBOutlet UILabel *textLabel;
@end
```

In the implementation I override initWithFrame:

```- (id)initWithFrame:(CGRect)frame
{
    //self = [super initWithFrame:frame];
    self = [JVUIKitUtils initWithNibName:@"MyView" withOwner:self];
    if( self )
    {
        NSLog(@"Created");
    }
    return self;
}
```

In I.B. I created a file named "MyView.xib" with a single view.  It has a label as a sub-view, and I created the label property by dragging it to the h file.


And in another file, I created this re-usable static method:

```+ (id)initWithNibName:(NSString*)nibName withOwner:(id)uiView
{
    id object = nil;
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:nibName owner:uiView options:nil]; // 1 object, out of scope
    for( id tempObject in bundle)
    {
        if( [tempObject isKindOfClass:[uiView class]] ) object = tempObject;
        break;
    }
    return object;
}
```

As you can see in the following screen shot, the bundle has one object reference, but it's out of scope.

And debugging:


This is my code for instantiation:

```subView = [[MyView alloc] initWithFrame:CGRectZero]; // ok
NSAssert(subView != nil, @"MyView was nil"); // fail
```

Any ideas on why the other S.O. poster was able to get it to work but this does not?

Comment: I have no idea what you're trying to do with `self = [JVUIKitUtils initWithNibName:@"MyView" withOwner:self]` but that is absolutely *WRONG*.

Comment: There's a difference between base SDK and deployment target. ARC works with a deployment target of iOS 4, but does it work with a base SDK of iOS 4? You shouldn't be using a base SDK of iOS 4 in any case.

Comment: Also, you can't use ARC with a base SDK of 4.3.

Comment: I'm trying to load the nib from a static class.  Why is this wrong?

Comment: @hwaxxer ARC works with any SDK down to iOS 4.

Comment: My mistake, base sdk is 5, but deployment target is 4.3.

Here is the accepted answer: The use of the owner seems a bit confusing in the way that you are loading a nib. It appears that you are trying to use the view as both the owner of the nib and the first object in it.

Are you trying to load MyView from your nib (i.e. is the class of the view inside your nib files defined as MyView) or are you trying to load a subview of MyView from the nib?

If the view inside your nib is a MyView, here's how to load it. Create this static method as a category on UIView:

```@implementation UIView (NibLoading)

+ (id)viewWithNibName:(NSString*)nibName
{
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:nibName owner:nil options:nil];
    if ([bundle count])
    {
        UIView *view = [bundle objectAtIndex:0];
        if ([view isKindOfClass:self])
        {
            return view;
        }
        NSLog(@"The object in the nib %@ is a %@, not a %@", nibName, [view class], self);
    }
    return nil;
}

@end
```

That will let you load any kind of view from a nib file (the view needs to be the first item defined in the nib). You would create your view like this:

```MyView *view = [MyView viewWithNibName:@"MyView"];
```

If the view inside the nib is not a MyView, but you want to load it as a subview of MyView, with MyView defined as the file's owner in the nib file, do it like this:

```@implementation UIView (NibLoading)

- (void)loadContentsFromNibName:(NSString*)nibName
{
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil];
    if ([bundle count])
    {
        UIView *view = [bundle objectAtIndex:0];
        if ([view isKindOfClass:[UIView class]])
        {
            //resize view to fit
            view.frame = self.bounds;

            //add as subview
            [self addSubview:view];
        }
        NSLog(@"The object in the nib %@ is a %@, not a UIView", nibName, [view class]);
    }
}

@end
```

Using that approach, just create your view as normal using initWithFrame, then call loadContentsFromNibName to loa the contents from a nib. You would load your view like this:

```MyView *view = [[MyView alloc] initWithFrame:CGRect(0,0,100,100)];
[view loadContentsFromNibName:@"MyView"];
```

Comment for this answer: You don't need to combine them. They are both slightly different variations of the same approach. Either should work (although mine is less code to implement ;-))
Comment for this answer: Because when you load the view from the nib, it's not the same as the view that you create with initWithFrame. Nib files actually contain their own copies of objects, which are loaded into the file's owner. When you initialise a view controller with a nib, you are actually loading the view controller's view object from within the nib, but when you initialise a view from a nib, you either have to get the whole object from the nib and return it (as in my first example) or you have to load a subview from the nib and then add that subview to the view you've created (as in my second example).
Comment for this answer: Don't use the file's owner for the first solution. In your nib file, you must set the view in your nib to be of class MyView, and then bind all the outlets from the sub-controls to your view itself, not to the file's owner. This is what I was trying to explain. The file's owner is the class that loads the view, which in the second case is MyView, but in the first case in *nothing* because you are loading and returning the entire view directly from the nib - you aren't loading it *in to* anything.
Comment for this answer: Yeah, it took me a while to get my head around it.
Comment for this answer: Will try this in a moment by combining it with clairware's answer.
Comment for this answer: The second implementation is what I'm after, will respond in a moment.
Comment for this answer: I tried out the second method, and the nib loads (thank you VERY much).  I'm able to print out the text I assign to the label.  I'm curious though, why do you use the line [self addSubview:view]; ?  It seems like it's adding another subview to the already created uiview.
Comment for this answer: I've converted my code to use the first (static) method you proposed, as I don't want the subview effect.  But for some reason the bundle loading throws an NSUnknownKey exception (this class is not key value-coding compliant for the key textLabel). However, I created the view with a drag from the label to the file's owner and I can clearly see the connection in the connections inspector.
Comment for this answer: Sweeeeet, thank you so much.  I'm surprised after all my searching there isn't and easy-to-find tutorial to load a UIView from a nib without a UIViewController.

Here is another answer: Your ```[JVUIKitUtils initWithNibName:@"MyView" withOwner:self]``` approach is all wrong. When an init method is called, the object is already allocated by the call to ```[MyView alloc]```. The reason why you chain the calls to the super init method is so that this will daisy chain all the way down to the NSObject init method, which simply returns the instance it is. 

By setting "self" to the (autoreleased) instance value returned by your ```JVUIKitUtils```, you are effectively setting it to a memory address other than what was allocated by the call to ```[MyView alloc]```, which generates the out of scope error. 

Instead, don't create the init method your are trying to create. You already have the method to create and initialize your nib-based view in your state method. I would change the name and do something like:

```+ (UIView)newViewWithNibName:(NSString*)nibName withClassType:(Class)uiView
{
    id object = nil;

    // you need some object to assign nib file owner to. use an NSObject.
    NSObject* owner = [[NSObject alloc] init];
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:nibName owner:owner options:nil]; // 1 object, out of scope
    for( id tempObject in bundle)
    {
        if( [tempObject isKindOfClass:] ) object = [tempObject retain];
        break;
    }
    [owner release];
    return object;
}
```

and then call it like:

```MyView* subView = [JVUIKitUtils viewWithNibName:@"MyView" withClassType:[MyView class]];
```

I haven't tested this code. Your milage might vary.


{'source': 'StackExchangeDataset', 'subset': 'stackoverflow.com', 'metadata': {'AnswerCount': 2, 'CommentCount': 6, 'ContentLicense': 'CC BY-SA 3.0', 'CreationDate': '2012:00:02/10/12', 'FavoriteCount': None, 'PostTypeId': 1, 'Tags': '<iphone><ios><uiview><bundle><nib>', 'Title': 'Why does this init method when loading a nib return an object out of scope?', 'ViewCount': 2122, 'id': 9224857, 'user_AboutMe': '<p>Tiger Coding provides mobile application consulting and development.</p>\n', 'user_AccountId': 273517.0, 'user_DisplayName': 'TigerCoding', 'user_Reputation': 8690.0, 'user_Views': 338.0, 'user_id': 565174.0}}