Почему этот метод init при загрузке nib возвращает объект из области видимости?
Почему этот метод init возвращает объект из области видимости?
Используя XCode 4.2, базовый SDK 4.3 и ARC, я пытаюсь загрузить UIView из ниба (а не UIViewController). Мне не нужно вообще использовать UIViewController в этом процессе.
После прочтения этого ответа на S.O. вопрос здесь, похоже, это можно сделать:
Как загрузить UIView с помощью файла nib, созданного с помощью 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" с одним представлением. Он имеет метку в качестве подзадачи, и я создал свойство label, перетащив его в 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 scope]()
Это мой код для создания экземпляра:
subView = [[MyView alloc] initWithFrame:CGRectZero]; // ok
NSAssert(subView != nil, @"MyView was nil"); // fail
Любые идеи о том, почему другой С.О. плакат смог заставить его работать, но это не так?
Ответы
Ответ 1
Использование владельца кажется немного запутанным в том, как вы загружаете наконечник. Похоже, что вы пытаетесь использовать представление как владельца элемента, так и первого объекта в нем.
Вы пытаетесь загрузить MyView из своего nib (т.е. является классом представления внутри ваших файлов nib, определенных как MyView), или вы пытаетесь загрузить подвью MyView из nib?
Если представление внутри вашего 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 не является 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"];
Ответ 2
Ваш подход [JVUIKitUtils initWithNibName:@"MyView" withOwner:self]
ошибочен. Когда вызывается метод init, объект уже назначается вызовом [MyView alloc]
. Причина, по которой вы связываете вызовы с методом суперинтеграции, заключается в том, что это приведет к полной цепочке вплоть до метода init NSObject, который просто возвращает экземпляр, который он есть.
Установив "self" в значение экземпляра (autoreleased), возвращаемое вашим JVUIKitUtils
, вы фактически устанавливаете его на адрес памяти, отличный от того, что было выделено вызовом [MyView alloc]
, который генерирует вне рамки ошибка.
Вместо этого не создавайте метод init, который вы пытаетесь создать. У вас уже есть способ создания и инициализации вашего представления на основе 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]];
Я не тестировал этот код. Ваше возражение может измениться.