2

我刚刚开始在苹果上编程 GUI(到目前为止,框架看起来不错,但我发现文档的信息量远不如其他文档......Qt、.Net、java 等)。

我遇到的问题之一是了解谁拥有什么。例如,如果我调用 CTLineRefCreateWithAttributedString,生成的 CTLineRef 是否拥有属性字符串?如果属性字符串是可变的,而我更改了它怎么办?这会弄乱 CTLineRef 吗?

文档一直没有启发性。

CTLineRef 的参考没有提供有关该主题的信息: https ://developer.apple.com/library/mac/#documentation/Carbon/Reference/CTLineRef/Reference/reference.html

一些示例不会释放字符串,我将其作为它拥有的指标: https ://developer.apple.com/library/mac/#documentation/StringsTextFonts/Conceptual/CoreText_Programming/Operations/Operations.html

一些示例确实释放了字符串,这表明它不是: https ://developer.apple.com/library/mac/#samplecode/CoreAnimationText/Listings/VectorTextLayer_m.html

(这个不是苹果,但他似乎更了解我) http://www.cocoanetics.com/2011/01/befriending-core-text/

那么字符串是否被复制?如果我使用 CFMutableAttributedStringRef 是否可以更改(我假设我必须在之后创建一个新的 CTLineRef)?

这是一个具体的例子,但这是我在无数地方遇到过的问题。任何帮助将不胜感激。我觉得必须有一些规则来管理这些事情,但我一点也不知道这些规则是什么或在哪里可以找到它们。

4

3 回答 3

4

您寻求的管理规则可在Core Foundation Memory Management Guide中找到。要带走的要点是,您实际上不需要知道 CTLineRef 是否拥有属性字符串。任何对象都可以有多个所有者。您需要担心的是您是否拥有字符串,以及最适合放弃所有权的位置。如果您在创建 CTLineRef 后完成了它,请随意释放它。

考虑其余的 CTLineRef 的实现细节。如果 CTLineRef 遵循该文档中规定的规则(它确实如此)并且需要保留属性字符串,它将在内部保留它。它也有可能在内部制作副本(这很可能),因此不再关心原件。也许它将字符串发送到火星上的服务器并在以后每次需要它时查询它(不太可能)。重要的一点是,不管它做什么,如果不再需要它,您可以安全地释放它。

至于可变性和复制行为,这有点模糊。你是对的,文档没有明确说明行为。文档明确指出该对象是不可变的这意味着几乎可以保证它确实复制了输入字符串(或将其内容解析为其他内容)。众所周知的最佳实践是,由于您提到的确切原因,对象应始终复制输入字符串 - 实现无法知道提供的字符串是否实际上是可变的。为了使实现保持健壮,它需要确保其内部状态不能从外部世界改变。做出这种保证的唯一方法是复制字符串。即使是可变类也是如此,

于 2012-08-03T16:28:18.047 回答
0

要回答您的问题,我们可以尝试在创建CTLine. 我们还可以尝试在更改字符串之前和之后打印行的描述。

CFMutableAttributedStringRef mas = CFAttributedStringCreateMutable(NULL, 0);
CFAttributedStringReplaceString(mas, CFRangeMake(0, 0), CFSTR("world"));
CTLineRef line = CTLineCreateWithAttributedString(mas);
NSLog(@"mas count = %ld", CFGetRetainCount(mas));
NSLog(@"line before change = %@", line);
CFAttributedStringReplaceString(mas, CFRangeMake(0, 0), CFSTR("hello "));
NSLog(@"line after change = %@", line);

查看对象的保留计数通常是徒劳的,但在这种情况下,它提供了丰富的信息:

2012-08-03 12:11:10.717 coretext[44780:f803] count before creating line = 1
2012-08-03 12:11:10.720 coretext[44780:f803] count after creating line = 1

由于之前和之后的保留计数为 1,并且我拥有一个引用(因为CFAttributedStringCreateMutable给了我一个拥有的引用),我知道我是字符串的唯一所有者,在我创建CTLine. 所以CTLine不保留字符串。它极不可能保留对字符串的引用而不保留它。

这是更改字符串之前的行描述:

2012-08-03 12:11:10.721 coretext[44780:f803] line = CTLine: run count = 1, string range = (0, 5), width = 28.6758, A/D/L = 9.24023/2.75977/0, glyph count = 5
{
    CTRun: string range = (0, 5), characters = { 0x0077, 0x006f, 0x0072, 0x006c, 0x0064 }, attributes =
<CFBasicHash 0x6d69ce0 [0x1227b38]>{type = mutable dict, count = 1,
entries =>
    2 : <CFString 0xab1b0 [0x1227b38]>{contents = "NSFont"} = CTFont <name: Helvetica, size: 12.000000, matrix: 0x0>
CTFontDescriptor <attributes: <CFBasicHash 0xd345ed0 [0x1227b38]>{type = mutable dict, count = 1,
entries =>
    1 : <CFString 0xabbd0 [0x1227b38]>{contents = "NSFontNameAttribute"} = <CFString 0x6d69720 [0x1227b38]>{contents = "Helvetica"}
}
>
}
}

我注意到描述不包含字符串,但包含字符数组。所以该行可能也不保留字符串的副本;它解析字符串以创建自己的私有表示。

这是更改字符串后的行描述:

2012-08-03 12:11:10.722 coretext[44780:f803] line = CTLine: run count = 1, string range = (0, 5), width = 28.6758, A/D/L = 9.24023/2.75977/0, glyph count = 5
{
    CTRun: string range = (0, 5), characters = { 0x0077, 0x006f, 0x0072, 0x006c, 0x0064 }, attributes =
<CFBasicHash 0x6d69ce0 [0x1227b38]>{type = mutable dict, count = 1,
entries =>
    2 : <CFString 0xab1b0 [0x1227b38]>{contents = "NSFont"} = CTFont <name: Helvetica, size: 12.000000, matrix: 0x0>
CTFontDescriptor <attributes: <CFBasicHash 0xd345ed0 [0x1227b38]>{type = mutable dict, count = 1,
entries =>
    1 : <CFString 0xabbd0 [0x1227b38]>{contents = "NSFontNameAttribute"} = <CFString 0x6d69720 [0x1227b38]>{contents = "Helvetica"}
}
>
}
}

我们可以看到该行没有更改其字形计数或字符数组。由此我们可以得出结论,当您更改字符串时,该行不会更改。您可以通过在更改字符串之前和之后实际绘制线条来进一步测试。我把它留给读者作为练习。

于 2012-08-03T17:17:59.063 回答
0

MutableString 的事情不应该是一个问题 - CTLine 文档明确指出它是不可变的。因此,在创建后无法更改它 - 这就是您需要知道的全部内容。它是否复制、保留或其他任何内容,或者您​​对创建 CTLine 的字符串所做的操作都没有关系 - 它是一个冻结的对象。

此外,只是为了支持马特的出色回答——保留/释放的全部意义在于摆脱对象必须“拥有”的可怕想法。以这种方式思考是没有帮助的。例如我们从来没有释放对象的责任,一个对象的生命周期是不可知的。保留/释放更像是一个简单的垃圾收集器。

你只需要遵循简单的规则,这些规则最好直接从 Apple 的源代码中学习,那里有很好的文档记录。始终遵循这些简单的规则。

因此,不,您不会在每个函数的文档中看到特定的内存管理指南。真的不需要。如果它还没有点击,你需要回到基础。

于 2012-08-03T18:36:55.187 回答