为什么加载 nib 时这个 init 方法会返回一个超出范围的对象?

发布于 2025-01-04 02:16:13 字数 1723 浏览 3 评论 0原文

为什么这个 init 方法返回一个超出范围的对象?

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

在这里阅读了对 SO 问题的回答后,看起来可以完成: 如何加载使用 Interface Builder 创建的 nib 文件的 UIView (用户“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;
}

在 IB 中,我创建了一个带有单个视图的名为“MyView.xib”的文件。它有一个标签作为子视图,我通过将其拖动到 h 文件来创建标签属性。 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 range

这是我的实例化代码:

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

关于为什么其他 SO 海报能够将其实现的任何想法有效,但这不起作用?

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.
MyView nib file setup

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:
MyView is out of scope

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

提笔落墨 2025-01-11 02:16:13

在加载笔尖的方式中,所有者的使用似乎有点令人困惑。看来您正在尝试将该视图用作笔尖的所有者和其中的第一个对象。

您是否尝试从 nib 加载 MyView(即 nib 文件中定义为 MyView 的视图类),或者您是否尝试从 nib 加载 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 从笔尖获取内容。您可以像这样加载您的视图:

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

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"];
纵山崖 2025-01-11 02:16:13

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

通过将“self”设置为 JVUIKitUtils 返回的(自动释放的)实例值,您可以有效地将其设置为除调用 [MyView alloc]< 所分配的内存地址之外的内存地址。 /code>,它会生成超出范围的错误。

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

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

我还没有测试过这段代码。您的里程可能会有所不同。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文