Подтвердить что ты не робот

Почему этот метод 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

Любые идеи о том, почему другой С.О. плакат смог заставить его работать, но это не так?

4b9b3361

Ответ 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]];

Я не тестировал этот код. Ваше возражение может измениться.