【问题标题】:Objective-C Safe Casting MacroObjective-C 安全转换宏
【发布时间】:2011-03-15 02:37:30
【问题描述】:

我在 Objective-C 中编写了一个宏来执行安全转换。到目前为止,它是这样的:

#define SAFE_CAST(OBJECT, TYPE) ([OBJECT isKindOfClass:[TYPE class]] ? (TYPE *) OBJECT: nil)

这确实很好用,但是如果有办法将 OBJECT 存储在变量中,这样它就不会被调用两次,那就太好了。例如,像这样使用宏:

NSString *str = SAFE_CAST([dictinary objectForKey:key], NSString);

宏展开时会产生类似的代码:

NSString *str = ([[dictinary objectForKey:key] isKindOfClass:[NSString class]] ? (NSString *) [dictinary objectForKey:key]: nil);

我希望它更像这样工作:

id obj = [dictionary objectForKey:key];
NSString *str = ([obj objectForKey:key] isKindOfClass[NSString class]] ? (NSString *) obj : nil);

谢谢。

【问题讨论】:

  • 我不明白这是什么意思。你说过你想用它来清理 plist,但如果你使用它,你肯定必须测试返回的对象是否为 nil?为什么不只测试对象是否 isKindOfClass: expectedClass?

标签: objective-c macros


【解决方案1】:

您可以使用名为 statement statement expressions 的 GCC 扩展来拥有

#define SAFE_CAST(OBJECT, TYPE) ({ id obj=OBJECT;[obj isKindOfClass:[TYPE class]] ? (TYPE *) obj: nil; })

也就是说,我认为在需要大量使用SAFE_CAST 的情况下,这通常是一种不好的方法。 永远不要将不同类的对象放在一个数组中;永远不要为不同类的 UI 对象重用操作消息 (IBAction)someAction:(id)sender。那么你通常不需要使用SAFE_CAST

【讨论】:

  • 现在我在从 plist 中读取信息时使用这个宏。我不保证 plist 的内容是有效的。就像任何其他演员一样;它可以被滥用,但它有它的用途。如果有更好的方法可以做到这一点,你能详细说明一下吗?根据您的回复,语句表达式看起来很有趣。它们可以与 LLVM 一起使用吗?
  • 是的,它适用于clang。至于plist,如果是用户提供的,我同意使用SAFE_CAST。但是如果不是,我觉得开发的时候还是用NSAssert比较好,这样我准备一个无效plist的时候程序就硬崩溃了;您的应用程序的发布版本不需要SAFE_CAST,因为您确保plist 是有效的。
  • 我不会说这是不好的方法。除非您在处理来自 NSNotification、json、推送通知等的 NSDIctionary 时可以接受崩溃。
【解决方案2】:

如果你真的认为你必须这样做,你可以使用一个函数:

#define SAFE_CAST(Object, Type) (Type *)cast_helper(Object, [Type class])
static id cast_helper(id x, Class c) {
    return [x isKindOfClass:c] ? x : nil;
}

【讨论】:

  • 这是一种有趣的方法,但是宏看起来更简洁一些。我试图避免让全局函数四处飘荡。另外,您能否详细说明“如果您真的认为必须这样做”是什么意思?其他几种语言支持安全转换,包括 C# 和 C++。为什么我不应该使用它们?
  • @helixed:为什么你认为一个全局宏悬挂比一个全局函数悬挂更好?
  • @helixed:对于上面提到的 plist 方法,它很适合,只是真正需要那些动态转换的情况很少见。不过,我没有看到全局函数的具体问题——我宁愿使用函数而不是只做直接文本处理的宏。
  • 我明白你的意思。除了一些可以忽略不计的开销之外,这似乎是一种更好的方法。谢谢。
  • @helixed:只要定义在调用时可见,就不会有任何开销 - 今天的编译器会积极内联,无论函数是否指定 inline
【解决方案3】:

所以这样写,把它包在 do { } while(0)

#define SAFE_CAST(OBJECT, TYPE, VAR) do { \
    id obj = OBJECT; \
    VAR = [obj isKindOfClass:[TYPE class]] ? (TYPE *) obj : nil; \
} while(0)

【讨论】:

  • 我无法在上面使用的用法中正确编译它。编译器输出:错误:'do'之前的预期表达式。
  • 你是对的,对不起。首先在 nil 之后有一个语法错误,missing ;其次,它需要独立存在。意思是,要么传入另一个分配三元运算输出的参数,要么以不同的方式进行。 :)
  • 不用担心。无论如何感谢您的回复。
  • 你能提醒你的读者(比如我)为什么我们要在一段时间内完成这个吗?谢谢!另外,这行得通吗?谢谢!
  • 您可能希望使用 do while 或固定表达式,以便您可以在需要表达式的地方使用它,并在遇到语句列表时抛出错误(如果语句条件是一个很好的例子)。
猜你喜欢
  • 1970-01-01
  • 2017-07-15
  • 2023-03-27
  • 2012-01-02
  • 2012-07-06
  • 2012-06-21
  • 1970-01-01
  • 1970-01-01
  • 2011-11-01
相关资源
最近更新 更多