gpt4 book ai didi

pointers - 分配 block 指针: differences between Objective-C vs C++ classes

转载 作者:行者123 更新时间:2023-12-02 19:19:27 25 4
gpt4 key购买 nike

我发现分配 block 对于 Objective-C 类参数和 C++ 类参数的行为有所不同。

假设我有这个简单的 Objective-C 类层次结构:

@interface Fruit : NSObject
@end

@interface Apple : Fruit
@end

然后我可以写这样的东西:

Fruit *(^getFruit)();
Apple *(^getApple)();
getFruit = getApple;

这意味着,相对于 Objective-C 类, block 其返回类型是协变的:返回更具体内容的 block 可以被视为“返回更通用内容的 block 的“子类”。在这里,可以将提供苹果的 getApple block 安全地分配给 getFruit block 。事实上,如果稍后使用,当您期待 Fruit * 时,总是可以保存以接收 Apple *。而且,从逻辑上讲,相反的情况是行不通的:getApple = getFruit; 无法编译,因为当我们真正想要一个苹果时,我们并不高兴只得到一个水果。

同样,我可以这样写:

void (^eatFruit)(Fruit *);
void (^eatApple)(Apple *);
eatApple = eatFruit;

这表明 block 在其参数类型上是协变的:可以在需要处理更具体参数的 block 的地方使用可以处理更通用参数的 block 。如果一个方 block 知道如何吃水果,它也会知道如何吃苹果。同样,反之亦然,这将无法编译:eatFruit = eatApple;

这一切都很好——在 Objective-C 中。现在让我们在 C++ 或 Objective-C++ 中尝试一下,假设我们有这些类似的 C++ 类:

class FruitCpp {};

class AppleCpp : public FruitCpp {};

class OrangeCpp : public FruitCpp {};

遗憾的是,这些 block 分配不再编译:

 FruitCpp *(^getFruitCpp)();
AppleCpp *(^getAppleCpp)();
getFruitCpp = getAppleCpp; // error!

void (^eatFruitCpp)(FruitCpp *);
void (^eatAppleCpp)(AppleCpp *);
eatAppleCpp = eatFruitCpp; // error!

Clang 提示“从不兼容的类型分配”错误。因此,相对于 C++ 类, block 的返回类型和参数类型似乎不变

这是为什么呢?我对 Objective-C 类所做的同样的论证不也适用于 C++ 类吗?我错过了什么?

最佳答案

由于 Objective-C 和 C++ 对象模型之间的差异,这种区别是有意为之的。特别是,给定一个指向 Objective-C 对象的指针,可以将该指针转换/转换为指向基类或派生类,而无需实际更改指针的值:无论如何,对象的地址都是相同的。

因为 C++ 允许多重继承和虚拟继承,所以 C++ 对象的情况并非如此:如果我有一个指向 C++ 类的指针,并且我将该指针强制转换/转换为指向基类或派生类,我可能会来调整指针的值。例如,考虑:

class A { int x; }
class B { int y; }
class C : public A, public B { }

B *getC() {
C *c = new C;
return c;
}

假设 getC() 中的新 C 对象在地址 0x10 处分配。指针“c”的值为0x10。在 return 语句中,指向 C 的指针需要调整为指向 C 中的 B 子对象。因为 B 在 C 的继承列表中位于 A 之后,所以它(通常)会在 A 之后布置在内存中,因此这意味着添加一个4 个字节的偏移量(== sizeof(A)) 到指针,因此返回的指针将为 0x14。类似地,将 B* 转换为 C* 将从指针中减去 4 个字节,以考虑 B 在 C 中的偏移量。在处理虚拟基类时,想法是相同的,但偏移量不再是已知的,编译时常量:在执行期间通过 vtable 访问它们。

现在,考虑一下这对作业的影响,例如:

C (^getC)();
B (^getB)();
getB = getC;

getC block 返回指向 C 的指针。要将其转换为返回指向 B 的指针的 block ,我们需要通过添加 4 个字节来调整每次调用该 block 返回的指针。这不是对 block 的调整;而是对 block 的调整。这是对 block 返回的指针值的调整。人们可以通过合成一个包装前一个 block 并执行调整的新 block 来实现这一点,例如,

getB = ^B() { return getC() }

这可以在编译器中实现,当使用需要调整的协变返回类型的函数覆盖虚拟函数时,编译器已经引入了类似的“thunk”。然而,使用 block 会导致一个额外的问题: block 允许与 == 进行相等比较,因此要评估是否“getB == getC”,我们必须能够查看由赋值“getB =”生成的 thunk getC”来比较底层 block 指针。同样,这是可以实现的,但需要更重量级的 block 运行时,能够创建(唯一的)thunk,能够对返回值(以及任何逆变参数)执行这些调整。虽然所有这些在技术上都是可行的,但成本(运行时大小、复杂性和执行时间)超过了 yield 。

回到 Objective-C,单继承对象模型永远不需要对对象指针进行任何调整:只有一个地址指向给定的 Objective-C 对象,无论指针的静态类型如何,因此协变/逆变从不需要任何重击, block 分配是一个简单的指针分配(+ ARC 下的 _Block_copy/_Block_release)。

关于pointers - 分配 block 指针: differences between Objective-C vs C++ classes,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15312774/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com