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

发布于 2024-11-01 00:18:28 字数 507 浏览 2 评论 0原文

这可能吗?

我的同事告诉我,像苹果通常所做的那样用前缀下划线命名 ivar 可能会导致一些问题。他说苹果可以在类中声明ivar,但不让它出现在公共头文件中。因此,如果我不小心命名的 ivar 与头文件中没有的“秘密 ivar”发生冲突,这将导致问题。

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

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

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

(我不知道我是否已经足够清楚地解释了我的问题......原谅我糟糕的英语......)

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.

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?

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.

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...)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

泪眸﹌ 2024-11-08 00:18:28

您的同事可能指的是类扩展中的实例变量。它们不会出现在类的(公共)界面中。例如,

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

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

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

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

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

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


也就是说,Apple 可能有一组与 SDK 一起分发的公共头文件,但使用声明其他实例变量的替代私有头文件。我发现这种情况不太可能发生,如果确实发生,可能会出现二进制不兼容的情况。我想只有苹果公司才能给出明确的答案。

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:

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

The compiler emits different symbol names for them:

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

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

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.


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.

感情旳空白 2024-11-08 00:18:28

我将再次调用@bbum,希望他能收到消息并过来纠正这个答案……但我会在这里尽力而为。

在旧的 ABI 中,您无法隐藏您的 ivars。编译器会在任何情况下捕获您正在讨论的冲突类型。也就是说,我发现使用其他命名约定来避免问题可能会更好。这样,当你发现你的超类中已经有一个 _data (就像我一样)时,你不必想出一些其他随机名称,或者在你不是故意的。尽管我最初不喜欢 Google 的尾随下划线,但自从我在一个大型项目中使用它后,它就越来越让我着迷。

您无法隐藏 ivars 的原因(如果您以某种方式隐藏,无论如何都不会发生名称冲突)是 ivars 过去只是结构偏移量。 Hampster Emporium 有一篇不错的短文解释了这一点。在运行时,没有 _window,只有偏移量 20。(同样,这是我所说的旧 ABI)。

对于方法来说情况并非如此。它们可能会发生碰撞。当这种情况发生时,这是一件坏事。您不会收到警告。苹果有一些没有前导下划线的私有方法,这真的让我很恼火。我与他们发生了碰撞,并且行为未定义。他们保留下划线;他们应该使用它。 (NSMutableArray 在一个类别中有一个私有的 -pop 方法,该方法的实现方式与我实现同名类别的方式相反。UINavigationController 中产生的错误code> 至少可以说很有趣。)

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

编辑 根据我与@Bvarious的讨论,我将此代码移至 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

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.

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.

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).

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.)

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.

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.

#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
时间海 2024-11-08 00:18:28

编译器会通知您是否与 ivars 存在名称冲突,但不会与方法名称发生冲突。

来自 Apple 开发者指南

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

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

From Apple's Developer's Guide:

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文