Почему этот UIView не добавляется как подпредставление при определенных обстоятельствах, а при других? - PullRequest
3 голосов
/ 09 января 2012

У меня есть UIView viewForRootVc, который является объявленным свойством для подкласса UIView NewView. NewView несет ответственность за инициализацию viewForRootVc, но затем подкласс NewViewSubClass устанавливает цвет фона. Как только все это будет сделано, контроллер корневого представления RootVc добавляет viewForRootVc в качестве подпредставления к своему представлению.

Но это не работает. viewForRootVc фактически не добавляется.

Однако, это ДЕЙСТВИТЕЛЬНО работает, если я делаю одну из следующих трех вещей (имейте в виду, что я использую ARC):

  1. Вместо того, чтобы устанавливать цвет фона viewForRootVc в NewViewSubClass, я установил его в NewView. И затем вместо инициализации экземпляра NewViewSubClass я просто инициализирую экземпляр NewView.
  2. При инициализации viewForRootVc в NewView я вызываю установщик (то есть self.viewForRootVc), а не просто использую прямое присваивание.
  3. Список viewForRootVc в виде ивара в заголовочном файле NewView.

Я не понимаю, почему любая из этих трех вещей необходима.

Вот код, который НЕ работает: RootVc.m

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    NewView *newView = [[NewViewSubClass alloc] initWithFrame:CGRectMake(0, 0, 300, 300)];

    //logs 0.00000, 0.00000    
    NSLog(@"%f, %f",newView.viewForRootVc.frame.size.width, newView.viewForRootVc.frame.size.height);

    [self.view addSubview:newView.viewForRootVc];

    //logs 0
    NSLog(@"subviews: %i",[self.view.subviews count]);
}

NewView.h

@property (nonatomic, retain) UIView *viewForRootVc;

NewView.m

@synthesize viewForRootVc;

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        viewForRootVc = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
    }
    return self;
}

NewViewSubClass.m

@synthesize viewForRootVc;

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        viewForRootVc.backgroundColor = [UIColor greenColor];
    }
    return self;
}

Одна вещь, которую я заметил, заключается в том, что если я укажу viewForRootVc в качестве ивара, мне не нужно помещать @synthesize viewForRootVc; в файл реализации NewViewSubClass.

Я всегда понимал объявленные свойства для эффективного достижения следующего:

  1. Разрешить другим объектам доступ к ним.
  2. Позволяет при необходимости вызывать сеттер.
  3. Если не использовать ARC, вызов установщика автоматически сохранит свойство, если вы указали retain в объявлении свойства.
  4. В ARC автоматически создается ivar для любых объявленных свойств.

Но ясно, что есть нечто большее, чем это. Я не уверен, что это проблема с областью действия, или если мой нерабочий код, приведенный выше, в итоге создает две разные версии viewForRootVc - возможно, ту, которая имеет правильный кадр, как указано в методе NewView init и тот, который имеет правильный цвет фона, как указано в методе NewViewSubClass init, но ни тот, ни другой не имеют ни правильной рамки, ни цвета.

Я очень надеюсь, что кто-то сможет раз и навсегда прояснить для меня последствия объявленных свойств по сравнению с ivars, а также вызвать установщик, чтобы установить объявленное свойство и присвоить ему значение напрямую.

1 Ответ

3 голосов
/ 09 января 2012

Таким образом, проблема заключается в этой строке в NewViewSubClass.m:

@synthesize viewForRootVc;

То, что в итоге делает компилятор, реализует новую пару getter / setter для подкласса. Это отличается от предыдущей пары getter / setter, которая была реализована для того же свойства в суперклассе.

Кроме того, здесь и в предыдущей директиве @synthesize происходит что-то еще, а именно то, что компилятор также генерирует ivar для поддержки этого свойства. Тем не менее, этот созданный ivar виден только в каждой конкретной реализации. Другими словами, каждая из двух директив @synthesize в итоге создает свой собственный отдельный ivar .

Итак, мы перейдем к этому фрагменту кода здесь, в NewViewSubClass.m:

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {

        // this ivar is the one from the subclass. It is still nil at this point!
        viewForRootVc.backgroundColor = [UIColor greenColor];
    }
    return self;
}

Здесь viewForRootVc - это ивар из подкласса. На данный момент это ноль. На самом деле, в вашем случае оно всегда равно нулю и никогда не устанавливается. Потому что, если вы посмотрите на этот код из NewView.m:

@synthesize viewForRootVc;

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {

        // Here viewForRootVc is the ivar generated for this class
        // this is not the same as the ivar with the same name in our subclass

        viewForRootVc = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
    }
    return self;
}

Суперкласс устанавливает свой ivar , называемый 'viewForRootVc', а не ivar с тем же именем, сгенерированным в подклассе.

Надеюсь, это имеет смысл. Итак, давайте рассмотрим три способа, которыми вы «исправили» это, чтобы точно понять, что произошло в каждом случае:

  1. Установить фон в NewView -> Это в основном использует правильный ivar (тот из NewView), который эффективно избегает проблемы, о которой мы говорили.

  2. Вызов установщика из NewView -> Это интересный. Вызывая установщик, вы фактически вызываете установщик, реализованный в подклассе . Это, в свою очередь, использует ivar в подклассе, и поэтому все «работает» (хотя, я думаю, это не идеально).

  3. Явное объявление ivar -> Это позволяет избежать проблемы по-другому. Объявляя ivar, вы не позволяете компилятору генерировать свой собственный ivar из директивы @synthesize. И поскольку ivars по умолчанию @protected, этот ivar также виден в подклассе, поэтому оба набора сеттеров / получателей в итоге используют один и тот же ivar.

Правильное исправление, IMO, позволяет ivars быть приватными для класса (@synthesized ivars скрыты и, следовательно, фактически приватны), и имеют подклассы, ссылающиеся на свойство getter / setter по мере необходимости. И тогда, конечно, не @synthesize свойство снова в вашем NewViewSubclass.m

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...