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

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

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

看到这个答案后,看起来可以这样做: 如何使用用Interface Builder创build的nib文件加载UIView (用户“MusiGenesis”的答案描述了这个过程)

我用一个标签创build了一个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中,我用一个视图创build了一个名为“MyView.xib”的文件。 它有一个标签作为子视图,并通过将其拖到h文件来创build标签属性。 MyView的笔尖文件设置

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

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

正如你在下面的屏幕截图中看到的那样,这个bundle有一个对象引用,但是它超出了范围。

并debugging: MyView超出范围

这是我实例化的代码:

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

任何想法,为什么其他SO海报能够得到它的工作,但这不?

所有者的使用似乎有点混淆的方式,你正在加载一个笔尖。 看起来您正在尝试使用该视图作为笔尖的所有者和其中的第一个对象。

你是否试图从你的笔尖加载MyView(即是你的笔尖文件定义为MyView中的视图的类)还是你想从笔尖加载MyView的子视图?

如果你的笔尖里面的视图是一个MyView,下面是如何加载它。 创build这个静态方法作为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文件加载任何types的视图(视图需要是在nib中定义的第一个项目)。 你会像这样创build你的视图:

 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创build视图,然后调用loadContentsFromNibName来从nib中获取内容。 你会像这样加载你的视图:

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

你的[JVUIKitUtils initWithNibName:@"MyView" withOwner:self]方法都是错误的。 当调用一个init方法时,该对象已经被调用[MyView alloc]分配了。 你把这些调用连接到超级初始化方法的原因是,这样就可以把它们连接到NSObject初始化方法,它只是简单地返回它的实例。

通过将“self”设置为由JVUIKitUtils返回的(自动发布的)实例值,实际上将其设置为除了调用[MyView alloc]所分配的内存地址以外的内存地址,这会生成超出范围的错误。

相反,不要创build您正在尝试创build的init方法。 你已经有了方法来在你的状态方法中创build和初始化你的基于脚本的视图。 我会改变名字,做一些事情:

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

我没有testing过这个代码。 你的青贮可能会有所不同。