可可框架可以在其公共头文件中隐藏一个ivar声明吗?

时间:2021-10-10 15:08:29

Is this possible ?

这可能吗 ?

My colleague told me that naming a ivar with prefix underscore like what apple mostly does may cause some problem. He said that apple can declare an ivar in a class and doesn't let it be in the public header file. So if i accidentally name an ivar that get collide with that "secret ivar" which is not in the header file , this will cause a problem.

我的同事告诉我,用苹果大多数人做的前缀下划线的ivar命名可能会引起一些问题。他说,苹果可以在一个类中声明一个ivar,并且不会让它出现在公共头文件中。因此,如果我不小心命名一个与头文件中没有的“秘密ivar”相撞的ivar,这将导致问题。

I don't know much about how frameworks works so I'm not sure about his explanation. I know apple only reserve the use of prefix underscore for only method name in there code guideline and many people do use prefix underscore when name an ivar and they said it's perfectly safe because if the name get collide the compiler would generate a error. I know this is true if the name of the ivar is in the header file. But what about that kind of "secret ivar" which is not in the public header file?

我不太了解框架是如何工作的所以我不确定他的解释。我知道苹果只保留使用前缀下划线仅用于代码指南中的方法名称,并且许多人在命名ivar时使用前缀下划线并且他们说它非常安全,因为如果名称发生碰撞,编译器将产生错误。我知道如果ivar的名称在头文件中,这是真的。但是那种不在公共头文件中的“秘密ivar”呢?

My colleague's another evidence for this is that if you dump an apple's application framework's header file and compare it with apple's public header file, you would find they don't match for many declaration of both methods and ivars.

我的同事的另一个证据是,如果你转储苹果的应用程序框架的头文件并将其与apple的公共头文件进行比较,你会发现它们与许多方法和ivars的许多声明都不匹配。

I'm really confused by this question and hope someone can provide some professional answer and if you can provide some reliable reference about this question , that would be great help.

我真的很困惑这个问题,并希望有人可以提供一些专业的答案,如果你能提供一些关于这个问题的可靠参考,那将是很大的帮助。

(I don't know whether I have explain my question clearly enough... Forgive my poor english...)

(我不知道我是否已经清楚地解释了我的问题......请原谅我可怜的英语......)

3 个解决方案

#1


3  

Your colleague might be referring to instance variables in class extensions. They do not show up in the (public) interface of the class. For instance,

您的同事可能在类扩展中引用实例变量。它们不会出现在类的(公共)接口中。例如,

// 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

Note that someone that has access to the public headers only won’t know that SomeClass has an instance variable called someIvar by reading the header file that declares that class. Also, it won’t be possible to access that instance variable in the same way as accessing an instance variable declared in the public interface. In fact, there won’t be a name collision in case a developer subclasses SomeClass and declares an instance variable named someIvar. For instance, this is valid:

请注意,有权访问公共标头的人不会通过读取声明该类的头文件来知道SomeClass有一个名为someIvar的实例变量。此外,无法以与访问公共接口中声明的实例变量相同的方式访问该实例变量。实际上,如果开发人员将SomeClass子类化并声明名为someIvar的实例变量,则不会发生名称冲突。例如,这是有效的:

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

In this case, any reference to someIvar in the implementation of SomeSubclass will refer to the instance variable declared in SomeSubclass only, and this instance variable is different from the one declared in the (private) class extension of SomeClass.

在这种情况下,SomeSubclass实现中对someIvar的任何引用都将引用仅在SomeSubclass中声明的实例变量,并且此实例变量与SomeClass的(private)类扩展中声明的实例变量不同。

The compiler emits different symbol names for them:

编译器为它们发出不同的符号名称:

  • _OBJC_IVAR_$_SomeClass.someIvar
  • _OBJC_IVAR _ $ _ SomeClass.someIvar
  • _OBJC_IVAR_$_SomeSubclass.someIvar
  • _OBJC_IVAR _ $ _ SomeSubclass.someIvar

and, upon instantiating an object of type SomeSubclass, both instance variables are located in different memory addresses.

并且,在实例化SomeSubclass类型的对象时,两个实例变量都位于不同的存储器地址中。

While an inspection of the binary might show that SomeClass has an instance variable called someIvar even though it’s not listed in the public header file, there won’t be a name collision in subclasses.

虽然检查二进制文件可能会显示SomeClass有一个名为someIvar的实例变量,即使它未在公共头文件中列出,但子类中也不会发生名称冲突。


That said, it might be possible that Apple have a set of public header files that they distribute with the SDK but use alternative, private header files that declare additional instance variables. I find that rather unlikely and there might be potential for binary incompatibility if that actually happens. I guess only Apple would be able to provide a definitive answer.

也就是说,Apple可能有一组公共头文件,它们随SDK一起分发,但使用另外的私有头文件来声明其他实例变量。我觉得这很不可能,如果实际发生这种情况,可能存在二进制不兼容的可能性。我想只有Apple才能提供明确的答案。

#2


2  

I'm going to invoke @bbum again hoping that he'll get the message and come and correct this answer.... But I'll do my best here.

我将再次调用@bbum,希望他能得到这个消息然后来纠正这个答案......但我会尽力而为。

In the old ABI, you could not hide your ivars. The compiler will catch the kinds of conflicts you're discussing in any case where they would matter. That said, I've found it's probably better to just avoid the problem by using some other naming convention. This way, when you find that there is already a _data in your superclass (as I have), you don't have to come up with some other random name, or accidentally access the superclass's private data when you didn't mean to. Despite my original dislike, Google's trailing underscore has grown on me since I used it in a large project.

在旧的ABI中,你无法隐藏你的伊娃。在任何重要的情况下,编译器都会捕获您正在讨论的各种冲突。也就是说,我发现通过使用其他一些命名约定来避免这个问题可能更好。这样,当你发现你的超类中已经有一个_data(就像我一样)时,你不必提出其他随机名称,或者在你不想要的时候不小心访问超类的私有数据。尽管我最初不喜欢,但自从我在一个大型项目中使用它以来,谷歌的尾随下划线已经在我身上发展。

The reason you couldn't hide your ivars (and if you did somehow, you wouldn't get name collisions anyway) is that ivars used to be just struct offsets. Hampster Emporium has a nice short post explaining this. At runtime, there is no _window, there is only offset 20. (Again, this is the old ABI I'm talking about).

你无法隐藏你的ivars的原因(如果你以某种方式,你不会得到名称冲突)是因为ivars过去只是结构偏移。 Hampster Emporium有一个很好的短信解释这个。在运行时,没有_window,只有偏移量20.(同样,这是我正在讨论的旧ABI)。

This is not true for methods. They can collide. This is a bad thing when it happens. You don't get a warning. It really annoys me that Apple has private methods that do not have leading underscores on them. I have collided with them and the behavior is undefined. They reserved underscore; they should use it. (NSMutableArray had a private -pop method in a category that was implemented backwards of how I implemented my category with the same name. The resulting bugs in UINavigationController were entertaining to say the least.)

对于方法而言,情况并非如此。他们可以碰撞。当它发生时这是一件坏事。你没有得到警告。让我感到很恼火的是,苹果公司的私人方法并没有引人瞩目。我与他们相撞并且行为未定义。他们保留了下划线;他们应该使用它。 (NSMutableArray在一个类别中有一个私有的-pop方法,后者实现了我使用相同名称实现类别的方法。在UINavigationController中产生的错误至少可以说是有趣的。)

That digression to the side, with the new ABI, it is now possible to have "hidden" ivars. You just declare them as synthesized properties in a private class extension. But through the magic of the new ABI (ok, not magic, just pointers), the ivars are safe even if they collide. But the synthesized methods aren't safe from collision. Note how directly accessing the ivar below gives you the expected behavior, but using the property gives a surprising result. I haven't worked out how a developer should best avoid or even detect this kind of situation.

对于新的ABI,这种离题,现在可能有“隐藏”的伊娃。您只需将它们声明为私有类扩展中的综合属性。但是通过新ABI的魔力(好吧,不是魔术,只是指针),即使它们发生碰撞,它们也是安全的。但合成的方法不能避免碰撞。请注意,如何直接访问下面的ivar可以获得预期的行为,但使用该属性会产生令人惊讶的结果。我还没有弄清楚开发人员应该如何最好地避免甚至发现这种情况。

EDIT Based on my discussion with @Bavarious, I moved this code to the version of clang that comes with Xcode 4 (most of my boxes are still on Xcode 3). It complains with all the kinds of loud errors you'd hope for. Particularly you get "Property someIvar is already implemented." So that bodes very well for using @property the way you'd want to.

编辑基于我与@Bavarious的讨论,我将此代码移动到Xcode 4附带的clang版本(我的大多数盒子仍在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

#3


0  

The compiler will let you know if there is a name * with ivars, but not with method names.

如果与ivars存在名称冲突,编译器将通知您,但不会与方法名称冲突。

From Apple's Developer's Guide:

来自Apple的开发人员指南:

Avoid the use of the underscore character as a prefix meaning private, especially in methods. Apple reserves the use of this convention. Use by third parties could result in name-space collisions; they might unwittingly override an existing private method with one of their own, with disastrous consequences. See “Private Methods” for suggestions on conventions to follow for private API.

避免使用下划线字符作为私有的前缀,尤其是在方法中。 Apple保留使用此约定。第三方使用可能会导致名称空间冲突;他们可能会无意中用自己的方法覆盖现有的私有方法,带来灾难性的后果。有关私有API遵循的约定的建议,请参阅“私有方法”。

#1


3  

Your colleague might be referring to instance variables in class extensions. They do not show up in the (public) interface of the class. For instance,

您的同事可能在类扩展中引用实例变量。它们不会出现在类的(公共)接口中。例如,

// 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

Note that someone that has access to the public headers only won’t know that SomeClass has an instance variable called someIvar by reading the header file that declares that class. Also, it won’t be possible to access that instance variable in the same way as accessing an instance variable declared in the public interface. In fact, there won’t be a name collision in case a developer subclasses SomeClass and declares an instance variable named someIvar. For instance, this is valid:

请注意,有权访问公共标头的人不会通过读取声明该类的头文件来知道SomeClass有一个名为someIvar的实例变量。此外,无法以与访问公共接口中声明的实例变量相同的方式访问该实例变量。实际上,如果开发人员将SomeClass子类化并声明名为someIvar的实例变量,则不会发生名称冲突。例如,这是有效的:

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

In this case, any reference to someIvar in the implementation of SomeSubclass will refer to the instance variable declared in SomeSubclass only, and this instance variable is different from the one declared in the (private) class extension of SomeClass.

在这种情况下,SomeSubclass实现中对someIvar的任何引用都将引用仅在SomeSubclass中声明的实例变量,并且此实例变量与SomeClass的(private)类扩展中声明的实例变量不同。

The compiler emits different symbol names for them:

编译器为它们发出不同的符号名称:

  • _OBJC_IVAR_$_SomeClass.someIvar
  • _OBJC_IVAR _ $ _ SomeClass.someIvar
  • _OBJC_IVAR_$_SomeSubclass.someIvar
  • _OBJC_IVAR _ $ _ SomeSubclass.someIvar

and, upon instantiating an object of type SomeSubclass, both instance variables are located in different memory addresses.

并且,在实例化SomeSubclass类型的对象时,两个实例变量都位于不同的存储器地址中。

While an inspection of the binary might show that SomeClass has an instance variable called someIvar even though it’s not listed in the public header file, there won’t be a name collision in subclasses.

虽然检查二进制文件可能会显示SomeClass有一个名为someIvar的实例变量,即使它未在公共头文件中列出,但子类中也不会发生名称冲突。


That said, it might be possible that Apple have a set of public header files that they distribute with the SDK but use alternative, private header files that declare additional instance variables. I find that rather unlikely and there might be potential for binary incompatibility if that actually happens. I guess only Apple would be able to provide a definitive answer.

也就是说,Apple可能有一组公共头文件,它们随SDK一起分发,但使用另外的私有头文件来声明其他实例变量。我觉得这很不可能,如果实际发生这种情况,可能存在二进制不兼容的可能性。我想只有Apple才能提供明确的答案。

#2


2  

I'm going to invoke @bbum again hoping that he'll get the message and come and correct this answer.... But I'll do my best here.

我将再次调用@bbum,希望他能得到这个消息然后来纠正这个答案......但我会尽力而为。

In the old ABI, you could not hide your ivars. The compiler will catch the kinds of conflicts you're discussing in any case where they would matter. That said, I've found it's probably better to just avoid the problem by using some other naming convention. This way, when you find that there is already a _data in your superclass (as I have), you don't have to come up with some other random name, or accidentally access the superclass's private data when you didn't mean to. Despite my original dislike, Google's trailing underscore has grown on me since I used it in a large project.

在旧的ABI中,你无法隐藏你的伊娃。在任何重要的情况下,编译器都会捕获您正在讨论的各种冲突。也就是说,我发现通过使用其他一些命名约定来避免这个问题可能更好。这样,当你发现你的超类中已经有一个_data(就像我一样)时,你不必提出其他随机名称,或者在你不想要的时候不小心访问超类的私有数据。尽管我最初不喜欢,但自从我在一个大型项目中使用它以来,谷歌的尾随下划线已经在我身上发展。

The reason you couldn't hide your ivars (and if you did somehow, you wouldn't get name collisions anyway) is that ivars used to be just struct offsets. Hampster Emporium has a nice short post explaining this. At runtime, there is no _window, there is only offset 20. (Again, this is the old ABI I'm talking about).

你无法隐藏你的ivars的原因(如果你以某种方式,你不会得到名称冲突)是因为ivars过去只是结构偏移。 Hampster Emporium有一个很好的短信解释这个。在运行时,没有_window,只有偏移量20.(同样,这是我正在讨论的旧ABI)。

This is not true for methods. They can collide. This is a bad thing when it happens. You don't get a warning. It really annoys me that Apple has private methods that do not have leading underscores on them. I have collided with them and the behavior is undefined. They reserved underscore; they should use it. (NSMutableArray had a private -pop method in a category that was implemented backwards of how I implemented my category with the same name. The resulting bugs in UINavigationController were entertaining to say the least.)

对于方法而言,情况并非如此。他们可以碰撞。当它发生时这是一件坏事。你没有得到警告。让我感到很恼火的是,苹果公司的私人方法并没有引人瞩目。我与他们相撞并且行为未定义。他们保留了下划线;他们应该使用它。 (NSMutableArray在一个类别中有一个私有的-pop方法,后者实现了我使用相同名称实现类别的方法。在UINavigationController中产生的错误至少可以说是有趣的。)

That digression to the side, with the new ABI, it is now possible to have "hidden" ivars. You just declare them as synthesized properties in a private class extension. But through the magic of the new ABI (ok, not magic, just pointers), the ivars are safe even if they collide. But the synthesized methods aren't safe from collision. Note how directly accessing the ivar below gives you the expected behavior, but using the property gives a surprising result. I haven't worked out how a developer should best avoid or even detect this kind of situation.

对于新的ABI,这种离题,现在可能有“隐藏”的伊娃。您只需将它们声明为私有类扩展中的综合属性。但是通过新ABI的魔力(好吧,不是魔术,只是指针),即使它们发生碰撞,它们也是安全的。但合成的方法不能避免碰撞。请注意,如何直接访问下面的ivar可以获得预期的行为,但使用该属性会产生令人惊讶的结果。我还没有弄清楚开发人员应该如何最好地避免甚至发现这种情况。

EDIT Based on my discussion with @Bavarious, I moved this code to the version of clang that comes with Xcode 4 (most of my boxes are still on Xcode 3). It complains with all the kinds of loud errors you'd hope for. Particularly you get "Property someIvar is already implemented." So that bodes very well for using @property the way you'd want to.

编辑基于我与@Bavarious的讨论,我将此代码移动到Xcode 4附带的clang版本(我的大多数盒子仍在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

#3


0  

The compiler will let you know if there is a name * with ivars, but not with method names.

如果与ivars存在名称冲突,编译器将通知您,但不会与方法名称冲突。

From Apple's Developer's Guide:

来自Apple的开发人员指南:

Avoid the use of the underscore character as a prefix meaning private, especially in methods. Apple reserves the use of this convention. Use by third parties could result in name-space collisions; they might unwittingly override an existing private method with one of their own, with disastrous consequences. See “Private Methods” for suggestions on conventions to follow for private API.

避免使用下划线字符作为私有的前缀,尤其是在方法中。 Apple保留使用此约定。第三方使用可能会导致名称空间冲突;他们可能会无意中用自己的方法覆盖现有的私有方法,带来灾难性的后果。有关私有API遵循的约定的建议,请参阅“私有方法”。