【问题标题】:Obj-C: C functions declared inside or outside @implementation block, what's the difference?Obj-C:在@implementation 块内部或外部声明的C 函数,有什么区别?
【发布时间】:2009-06-22 17:43:30
【问题描述】:

在 Objective-C 类的实现块 (@implementation ... @end) 内部或外部声明的 C 函数(静态或非静态)有什么区别?

这是真的吗?:

如果你需要直接在对象内部戳,你可以把这个函数放在你类的@implementation块里面,然后你可以用C箭头操作符访问实例变量。但这有点淘气,所以为了保持你的本质纯度,你应该在你的对象上使用方法调用。讲道结束。这是邪恶的:

@implementation OblateSphereoid

void drawEggThunk (DrawingContext *context, Rect areaToDraw, void *userData)
{
BWOblateSphereoid *dealie = (BWOblateSphereoid *)userData;
dealie->_frognatz = [NSColor plaidColor];
// and more stuff.
} // drawEggThunk

...
@end // OblateSphereoid

我可以通过这种方式在函数(在同一个类中声明)中访问我的类的实例变量吗?

【问题讨论】:

  • 赞成是一个有趣的问题(尽管可能是非常糟糕的做法)只是出于好奇,您只能在课堂上调用 drawEggThunk 吗?
  • 我不能,该函数是从 C 子系统调用的回调函数,如您所见,我在设置回调时使用 *userData 传递对 self 的引用。

标签: c objective-c cocoa-touch


【解决方案1】:

虽然这是合法的,但我不明白为什么在您描述的那种情况下需要它(这是一个丑陋的解决方案)。为什么不能直接打电话:

[dealie setFrognatz:[NSColor plaidColor]];

如果您通常不提供-setFrognatz:,只需在 .m 中声明它,但在此函数定义上方,将其设为私有方法。 (在这种情况下,它是否在 @implementation 块中实际上并不重要。)

@interface BWOblateSphereoid ()
- (void)setFrognatz:(NSColor *)acolor
@end

@implementation OblateSphereoid

void drawEggThunk (DrawingContext *context, Rect areaToDraw, void *userData)
{
    BWOblateSphereoid *dealie = (BWOblateSphereoid *)userData;
    [dealie setFrognatz:[NSColor plaidColor]];
    // and more stuff.
} // drawEggThunk

...
@end // OblateSphereoid

-> 表示法在一些地方会有所帮助。最关键的是实现-copyWithZone:,它可以是absolutely required(这个要求真正强调了为什么我讨厌任何像NSCopyObject() 这样使用原始内存的ObjC 代码)。但在大多数情况下,我建议反对->,原因与我总是推荐访问器的原因相同。即使在您需要通过引用 C 函数(-> 的另一个常见用法)来传递 ivar 的情况下,我更喜欢使用临时变量,然后再分配它。

我认为 ivars 默认不是 @private 的事实是 ObjC 中的一个错误......我将 @private 放在每个 @interface 块的顶部,这样就避免了几个讨厌的错误。

顺便说一句,您编写的解决方案可能会泄漏NSColor。也许是,也许不是,但访问者是肯定的。

【讨论】:

  • “私有”方法是解决根本问题的答案(在没有中断封装的情况下访问函数内的 ivars):) 但是...... @impementation 内部和外部的函数之间存在一些差异?
  • 顺便说一下,objc ivars 默认是@protected,这是完美的范围:对类本身和子类可见,对所有其他对象隐藏。
  • @implementation 内部和外部没有区别。
  • 我不同意子类应该能够看到其超类的 ivars。他们应该使用访问器(并且 ObjC 需要在方法上使用 @protected 以便我们可以更轻松地实现它)。访问你的超类的 ivars 会导致一个令人讨厌的错误机会(我已经把我烧死了)。您提升了一个超类,但忘记从子类 dealloc 中删除 ivar 版本。当你dealloc时,你和你的超类释放了ivar并且你崩溃了。 @private 在编译时捕获它。您应该始终对所有内容使用访问器,因此几乎没有充分的理由访问您的超类的 ivars。
【解决方案2】:

如果它在您的@implementation 中,它将正常工作。我建议您将其设为普通方法,但它确实有效。

如果你把它从你的实现中去掉,你会得到这个警告:警告:实例变量''是@protected;这将是未来的一个硬错误。您可以通过在标头中的 iVar 之前放置 @public 来避免此错误......就像这样:

@public 
int myInt;

这使您可以在对象出现的任何地方访问变量,如 C(指针)结构,而不会发出警告。

所以它是可行的,但不推荐!!!

【讨论】:

  • 我认为这也可能是一个不好的做法,但我不想为了封装而将 ivar 公开,但我的 C 回调函数必须能够看到实例 ivar。
  • 那你该做你该做的了……你不必把你所有的iVar都声明为public,你可以混搭。
猜你喜欢
  • 2015-02-10
  • 2022-08-26
  • 2019-06-14
  • 1970-01-01
  • 2020-06-17
  • 2013-03-23
  • 1970-01-01
  • 2012-10-02
  • 1970-01-01
相关资源
最近更新 更多