【问题标题】:Why does this init method when loading a nib return an object out of scope?为什么在加载 nib 时这个 init 方法会返回一个超出范围的对象?
【发布时间】:2012-03-02 17:59:39
【问题描述】:

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

使用 XCode 4.2、4.3 的基本 SDK 和 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 文件中来创建标签属性。

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

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

正如您在以下屏幕截图中所见,捆绑包有一个对象引用,但它超出了范围。

和调试:

这是我的实例化代码:

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

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

【问题讨论】:

  • 我不知道你想用self = [JVUIKitUtils initWithNibName:@"MyView" withOwner:self]做什么,但这绝对是错误
  • 我正在尝试从静态类加载笔尖。为什么这是错误的?
  • 另外,您不能将 ARC 与 4.3 的基础 SDK 一起使用。
  • @hwaxxer ARC 适用于任何 SDK 到 iOS 4。
  • 基础 SDK 和部署目标之间存在差异。 ARC 适用于 iOS 4 的部署目标,但它是否适用于 iOS 4 的基本 SDK?在任何情况下,您都不应该使用 iOS 4 的基础 SDK。

标签: iphone ios uiview bundle nib


【解决方案1】:

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

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

如果您的 nib 中的视图是 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里面的view不是MyView,但是你想把它作为MyView的子视图加载,并且在nib文件中MyView定义为文件的所有者,可以这样操作:

@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"];

【讨论】:

  • 稍后将通过将其与 clairware 的答案相结合来尝试。
  • 你不需要合并它们。它们都是相同方法的略有不同的变体。任何一个都应该工作(尽管我的代码较少实现;-))
  • 第二个实现是我所追求的,稍后会回复。
  • 我尝试了第二种方法,并且笔尖加载(非常感谢)。我能够打印出分配给标签的文本。不过我很好奇,你为什么要使用 [self addSubview:view]; ?似乎它正在向已经创建的 uiview 添加另一个子视图。
  • 因为当你从 nib 加载视图时,它和你用 initWithFrame 创建的视图是不一样的。 Nib 文件实际上包含它们自己的对象副本,这些副本被加载到文件的所有者中。当您使用 nib 初始化视图控制器时,实际上是从 nib 中加载视图控制器的视图对象,但是当您从 nib 初始化视图时,您要么必须从 nib 获取整个对象并返回它(如在我的第一个示例中)或者您必须从 nib 加载一个子视图,然后将该子视图添加到您创建的视图中(如在我的第二个示例中)。
【解决方案2】:

您的[JVUIKitUtils initWithNibName:@"MyView" withOwner:self] 方法完全错误。当调用 init 方法时,对象已经通过调用[MyView alloc] 分配。将调用链接到 super 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]];

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-10
    • 2012-04-22
    • 2019-02-13
    相关资源
    最近更新 更多