Может ли инфраструктура какао скрыть объявление ivar в своем общедоступном заголовочном файле? - PullRequest
3 голосов
/ 12 апреля 2011

Возможно ли это?

Мой коллега сказал мне, что присвоение имени ивару с префиксом, подчеркивающим то, что в основном делает яблоко, может вызвать некоторые проблемы. Он сказал, что apple может объявить ivar в классе и не позволяет ему находиться в публичном заголовочном файле. Поэтому, если я случайно назову ивара, который сталкивается с этим «секретным иваром», которого нет в заголовочном файле, это вызовет проблему.

Я не знаю много о том, как работают фреймворки, поэтому я не уверен в его объяснении. Я знаю, что Apple только зарезервировала использование подчеркивания префикса только для имени метода в этом руководстве, и многие люди используют подчеркивание префикса при именовании ивара, и они сказали, что это совершенно безопасно, потому что, если имя получит конфликт, компилятор выдаст ошибку. Я знаю, что это правда, если имя ивара находится в заголовочном файле. Но как насчет такого «секретного ивара», которого нет в публичном заголовочном файле?

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

Меня действительно смущает этот вопрос, и я надеюсь, что кто-то может дать какой-то профессиональный ответ, и если вы сможете дать надежную ссылку на этот вопрос, это было бы очень полезно.

(Я не знаю, достаточно ли я объяснил свой вопрос ... простите мой плохой английский ...)

Ответы [ 3 ]

3 голосов
/ 12 апреля 2011

Ваш коллега может ссылаться на переменные экземпляра в расширениях класса. Они не отображаются в (публичном) интерфейсе класса. Например,

// SomeClass.h -- this is a publicly distributed file
@interface SomeClass : NSObject
@end

// SomeClass.m -- this is known only to the framework developer
@interface SomeClass() {
    NSString *someIvar;
}
@end

@implementation SomeClass
- (id)init {
    self = [super init];
    if (self) someIvar = @"Default";
    return self;
}
@end

Обратите внимание, что кто-то, имеющий доступ к общедоступным заголовкам, не узнает, что SomeClass имеет переменную экземпляра с именем someIvar, прочитав файл заголовка, который объявляет этот класс. Кроме того, невозможно получить доступ к этой переменной экземпляра так же, как к переменной экземпляра, объявленной в общедоступном интерфейсе. Фактически, не будет конфликта имен, если разработчик подклассов SomeClass и объявит переменную экземпляра с именем someIvar. Например, это действительно:

// SomeSubclass.h
@interface SomeSubclass : SomeClass {
    NSString *someIvar;
}
@end

В этом случае любая ссылка на someIvar в реализации SomeSubclass будет ссылаться на переменную экземпляра, объявленную только в SomeSubclass, и эта переменная экземпляра отличается от той, которая объявлена ​​в (приватном) расширении класса SomeClass.

Компилятор выдает для них разные имена символов:

  • _OBJC_IVAR_$_SomeClass.someIvar
  • _OBJC_IVAR_$_SomeSubclass.someIvar

и при создании экземпляра объекта типа SomeSubclass обе переменные экземпляра расположены по разным адресам памяти.

Хотя проверка двоичного кода может показать, что SomeClass имеет переменную экземпляра с именем someIvar, даже если она не указана в открытом заголовочном файле, в подклассах не будет конфликта имен.


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

2 голосов
/ 12 апреля 2011

Я собираюсь снова вызвать @bbum в надежде, что он получит сообщение, придет и исправит этот ответ .... Но я сделаю все возможное здесь.

В старом ABI,Вы не могли скрыть свои ивары.Компилятор поймает типы конфликтов, которые вы обсуждаете в любом случае, когда они имеют значение.Тем не менее, я обнаружил, что, вероятно, лучше просто избежать этой проблемы, используя другое соглашение об именах.Таким образом, когда вы обнаружите, что в вашем суперклассе уже есть _data (как у меня), вам не нужно придумывать какое-то другое случайное имя или случайно получать доступ к личным данным суперкласса, когда вы не имели в видук.Несмотря на мою первоначальную неприязнь, нижнее подчеркивание Google выросло на меня с тех пор, как я использовал его в большом проекте.

Причина, по которой вы не могли скрыть свои ивары (и если бы вы сделали это каким-то образом, вы не получите столкновения именв любом случае) то, что ивары раньше были просто структурными смещениями.У Hampster Emporium есть хороший короткий пост , объясняющий это.Во время выполнения нет _window, есть только смещение 20. (Опять же, это старый ABI, о котором я говорю).

Это не так для методов.Они могут столкнуться.Это плохо, когда это происходит.Вы не получите предупреждение.Меня действительно раздражает, что у Apple есть частные методы, которые не имеют подчеркивания.Я столкнулся с ними, и поведение не определено.Они защищены подчеркивание;они должны использовать это.(У NSMutableArray был частный метод -pop в категории, который был реализован в обратном направлении относительно того, как я реализовал мою категорию с тем же именем. Получающиеся ошибки в UINavigationController были по меньшей мере интересными.)

Это отступление в сторону, с новым ABI, теперь возможно иметь «скрытые» ивары.Вы просто объявляете их как синтезированные свойства в расширении частного класса.Но благодаря магии нового ABI (хорошо, не магия, только указатели), ивары безопасны, даже если они сталкиваются. Но синтезированные методы не защищены от столкновения.Обратите внимание, что прямой доступ к ivar ниже дает вам ожидаемое поведение, но использование свойства дает удивительный результат.Я так и не понял, как разработчику лучше всего избегать или даже обнаруживать подобные ситуации.

EDIT Основываясь на моем обсуждении с @Bavarious, я переместил этот код вверсия clang, которая идет с Xcode 4 (большинство моих коробок все еще на Xcode 3).Он жалуется на всевозможные громкие ошибки, на которые вы надеетесь.В частности, вы получите «Свойство someIvar уже реализовано».Так что это очень хорошо для использования @property так, как вы хотите.

#import <Foundation/Foundation.h>

// SomeClass.h
@interface SomeClass : NSObject
@end

// SomeClass.m
@interface SomeClass ()
@property (copy) NSString *someIvar;
@end

@implementation SomeClass
@synthesize someIvar;
- (id)init {
    self = [super init];
    if (self) someIvar = @"SomeClass";
    return self;
}

- (void)print
{
    NSLog(@"Superclass=%@:%@", someIvar, [self someIvar]);
}
@end

// SubClass.h
@interface SubClass : SomeClass
{
    NSString *someIvar;
}
@property (copy) NSString *someIvar;
@end

// SubClass.m
@implementation SubClass
@synthesize someIvar;
- (id)init {
    self = [super init];
    if (self) someIvar = @"SubClass";
    return self;
}

- (void)print
{
    [super print];
    NSLog(@"Subclass=%@:%@", someIvar, [self someIvar]);
}

@end

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    SubClass *subclass = [[SubClass alloc] init];
    [subclass print];
    [pool drain];
    return 0;
}


Superclass=SomeClass:SubClass    <== Note the mismatch
Subclass=SubClass:SubClass
0 голосов
/ 12 апреля 2011

Компилятор сообщит вам, если есть конфликт имен с ivars, но не с именами методов.

От Руководство разработчика Apple :

Избегайте использования символа подчеркивания в качестве префикса, означающего приватный, особенно в методах. Apple оставляет за собой право использовать это соглашение.Использование третьими лицами может привести к столкновениям пространства имен;они могут невольно переопределить существующий частный метод своим собственным, что приведет к катастрофическим последствиям.См. «Частные методы» для предложений по соглашениям для частного API.

...