为什么这个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属性。
在另一个文件中,我创建了这个可重用的静态方法:
+ (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.的任何想法海报能够让它工作,但这不是吗?
答案 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可能会有所不同。