为什么加载nib时这个init方法会将对象返回到范围之外?

时间:2012-02-10 08:31:14

标签: iphone ios uiview bundle nib

为什么这个init方法会将一个对象退出范围?

使用XCode 4.2,基础SDK 4.3和ARC,我正在尝试从nib(不是UIViewController)加载UIView。我在这个过程中根本不需要使用UIViewController。

读完S.O.的答案后这里有问题,看起来可以做到: How to load a UIView using a nib file created with Interface Builder (用户“MusiGenesis”的答案描述了这个过程)

我创建了一个带有单个标签的UIView子类:

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

在实现中,我重写了initWithFrame:

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

在I.B.我用一个视图创建了一个名为“MyView.xib”的文件。它有一个标签作为子视图,我通过将它拖动到h文件创建了label属性。 MyView nib file setup

在另一个文件中,我创建了这个可重用的静态方法:

+ (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;
}

正如您在下面的屏幕截图中看到的那样,该包具有一个对象引用,但它超出了范围。

调试: MyView is out of scope

这是我的实例化代码:

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

关于为什么其他S.O.的任何想法海报能够让它工作,但这不是吗?

2 个答案:

答案 0 :(得分:3)

使用所有者在加载笔尖的方式上似乎有点混乱。您似乎正在尝试将视图用作nib的所有者和其中的第一个对象。

您是否尝试从笔尖加载MyView(即,您的nib文件中定义为MyView的视图类)或者您是否尝试从笔尖加载MyView的子视图?

如果你的笔尖内的视图是MyView,下面是如何加载它。在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

这将允许您从nib文件加载任何类型的视图(视图需要是nib中定义的第一个项目)。您可以像这样创建视图:

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

如果nib中的视图不是MyView,但您想将其作为MyView的子视图加载,并将MyView定义为nib文件中的文件所有者,请执行以下操作:

@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

使用该方法,只需使用initWithFrame正常创建视图,然后调用loadContentsFromNibName从nib中获取内容。您可以像这样加载视图:

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

答案 1 :(得分:0)

您的[JVUIKitUtils initWithNibName:@"MyView" withOwner:self]方法完全错误。调用init方法时,该对象已通过调用[MyView alloc]进行分配。您将调用链接到超级init方法的原因是,它将菊花链一直向下到NSObject init方法,该方法只返回它的实例。

通过将“self”设置为JVUIKitUtils返回的(自动释放的)实例值,您实际上将其设置为除[MyView alloc]调用所分配的内存地址之外的内存地址。超出范围错误。

相反,不要创建您尝试创建的init方法。您已经有了在state方法中创建和初始化基于nib的视图的方法。我会更改名称并执行以下操作:

+ (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;
}

然后将其称为:

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

我还没有测试过这段代码。你的milage可能会有所不同。