【问题标题】:Is it possible to use a single typedef to define a block that takes no parameters and returns a block?是否可以使用单个 typedef 来定义不带参数并返回块的块?
【发布时间】:2019-02-09 18:14:20
【问题描述】:

类似/跟进这个问题: Syntax to define a Block that takes a Block and returns a Block in Objective-C

我知道这不是我想做的事情,但我想了解为什么我不能使用单个 typedef 来定义:

typedef void (^XYZSimpleBlock)(void);
typedef XYZSimpleBlock (^ComplexBlock)(void);

为什么上面的有效,而这个无效?

typedef void(^)(void) (^ComplexBlock)(void);

如果我通过类似的答案,我发现我可以尝试像编译器一样解析它,但我是一个糟糕的编译器。

按照 C 的左右规则,第一个标识符不是 ComplexBlock

typedef void(^)(void) (^ComplexBlock)(void);
// parser reads left right and finds ComplexBlock first

然后它会向右找到声明的结尾,然后向左直到找到插入符号,然后向右解释参数列表。

这将我们带到

 typedef void(^)(void) (^ComplexBlock)(void);
//       | unhandled   |compiled/interpreted| 

此时它无法将void(^)(void) 读取为返回类型是怎么回事?

让我感到困惑的是,使用 typedef 基本上只是一个 #define,所以它不应该与上面的基本相同吗?

typedef void (^XYZSimpleBlock)(void);
same as:
void(^)(void)    
so:
typedef XYZSimpleBlock (^ComplexBlock)(void);
should be the same as:
typedef void(^)(void) (^ComplexBlock)(void);

我在这里遗漏了一些东西......

【问题讨论】:

  • 这不是C代码!我想你可以留下 c-tag。
  • 好电话,正在删除。我认为这可能与底层的 c 编译器有关,但可能不足以证明标签的合理性。

标签: objective-c


【解决方案1】:

正确的语法是:

typedef void (^(^ComplexBlock)(void))(void);

当您像这样构造复杂类型的声明时,从更简单的类型开始并用“表达式”代替标识符会有所帮助。

例如,声明一个不带参数并返回void的函数:

void a_func(void);

现在,我们要声明一个函数指针a_func_ptr,它是指向同一类型函数的指针。我们希望表达式 *a_func_ptra_func 的类型相同,因此我们将括号中的表达式放到上面并得到:

void (*a_func_ptr)(void);

如果我们想要一个块指针而不是函数指针,我们使用^而不是*

void (^a_block_ref)(void);

(人们发现块语法令人沮丧的一个原因是人们实际上从未将表达式^a_block_ref 写入“取消引用”块变量,因此实现这种飞跃并不明显。)

采用上述方法并从中制作typedef(而不是变量声明)是您获得以下内容的方式:

typedef void (^XYZSimpleBlock)(void);

但是,让我们暂时回到函数指针a_func_ptr。假设您要声明一个返回函数指针的函数func_returns_func。您将函数调用表达式放在a_func_ptr 所在的位置。也就是说,您将func_returns_func() 放入:

void (*func_returns_func())(void);

现在,在声明函数时,您需要使用 void 来表示无参数,因此您可以这样做:

void (*func_returns_func(void))(void);

请注意,您不会在标识符左侧收集或隔离返回类型。它围绕着标识符。

现在您将* 更改为^ 以获得一个返回块引用而不是函数指针的函数:

void (^func_returns_block(void))(void);

让我们创建一个指向返回块引用的函数的指针。将(*ptr_to_func_returns_block) 放入func_returns_block 并获取:

void (^(*ptr_to_func_returns_block)(void))(void);

同样,将* 替换为^ 以获得对返回块引用的块的引用:

void (^(^block_returns_block)(void))(void);

我们到了。只需更改名称即可:

void (^(^ComplexBlock)(void))(void);

【讨论】:

  • 慢拍 很好的描述。后续问题,块 ref 和函数 ptr 在内存方面有什么区别?是因为它们是对象,因此每个块都有方法共享内存和新内存吗?这是一个公平的描述吗?
  • 嗯,从技术上讲,这是一个实现细节。实际上,函数指针只是函数代码的第一条指令的地址。块引用是一个指向对象的指针,该对象跟踪块代码的匿名函数的函数指针、任何捕获的变量和其他簿记内容。对于任何一个,代码通常是静态的。它是可执行文件的一部分,当它被加载时,代码就被加载了。我认为这就是您所说的“共享内存”的意思,尽管该术语通常有其他含义。
猜你喜欢
  • 2010-10-01
  • 2010-12-16
  • 2011-07-30
  • 2010-09-13
  • 1970-01-01
  • 1970-01-01
  • 2014-05-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多