CTLineRef是否拥有属性字符串?

时间:2012-08-03 16:01:31

标签: objective-c cocoa core-graphics ctlineref

我刚刚开始在apple上编程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)?

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

3 个答案:

答案 0 :(得分:4)

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

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

至于可变性和复制行为,这有点模糊。你的文档没有明确说明你的行为是对的。文档显式的,该对象是不可变的。这意味着它完全可以保证 实际上复制输入字符串(或将其内容解析为其他内容)。通常理解的最佳做法是,对象应始终使输入字符串的副本出于您提及的确切原因 - 实现无法知道提供的字符串是否实际上是可变的。为了使实现保持稳健,需要确保其内部状态不能从外部世界改变。保证这一点的唯一方法是复制字符串。即使是可变类,CTLineRef也不是这样。

答案 1 :(得分: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"}
}
>
}
}

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

答案 2 :(得分:0)

MutableString不应该是一个问题 - CTLine文档明确指出它是不可变的。因此在创建之后无法更改它 - 这就是您需要知道的全部内容。无论是复制,保留还是其他什么,或者你对你创建CTLine的字符串做了什么都没关系 - 它是一个冻结的对象。

另外,只是为了支持马特的优秀答案 - 保留/释放的全部目的是摆脱一个对象必须被“拥有”的可怕想法。以这种方式思考是没有帮助的。例如,我们永远不负责释放对象,对象的生命周期是不可知的。保留/释放更像是一个简单的垃圾收集器。

您只需遵循简单的规则,这些规则最好直接从Apple的来源学习,并在其中有详细记录。始终遵循这些简单的规则。

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