Xamarin.ios 为什么UIView.StringSize需要在UI线程上运行?

Xamarin.ios 为什么UIView.StringSize需要在UI线程上运行?,xamarin.ios,Xamarin.ios,如果我从一些异步代码中运行UIView.StringSize,比如任务.ContinueWith,它将以UIKitThreadAccessException爆炸,因为该方法以调用UIApplication.EnsureWithRead(MonoDevelopment中的“转到声明”;我不确定许可证是否允许我在此处发布它) 如果我将这个简化版本包装在InvokeOnMainThread中,一切都很好,但我确实有一些时候想在没有调用的情况下测量一些文本。同样,我完全理解,在以前调用异步代码中的某些内

如果我从一些异步代码中运行
UIView.StringSize
,比如
任务.ContinueWith
,它将以
UIKitThreadAccessException
爆炸,因为该方法以调用
UIApplication.EnsureWithRead
(MonoDevelopment中的“转到声明”;我不确定许可证是否允许我在此处发布它)

如果我将这个简化版本包装在
InvokeOnMainThread
中,一切都很好,但我确实有一些时候想在没有调用的情况下测量一些文本。同样,我完全理解,在以前调用异步代码中的某些内容时,它为我省去了很多麻烦,但是在这种情况下,这里使用
EnsureUIThread
似乎是不必要的。如果我简单地将该调用重申为对
NSString
类的一次点击,它将愉快地运行在UI线程之外

Task.Factory.StartNew(() => {
    // Outputs expected size data: "{Width=##, Height=##}".
    using (NSString nssSomeString = new NSString(someString)) {
        SizeF textSize = nssSomeString.StringSize(someFont, new SizeF(someView.Bounds.Width, float.MaxValue), UILineBreakMode.WordWrap);
        Console.WriteLine(textSize);
    }
});
UIView.StringSize
的代码似乎做了大致相同的
NSString
工作,并且似乎没有任何明显的面向UI线程的内容。我是否缺少需要从UI线程调用此版本方法的内容

编辑(2013-01-17):
我只是想看看他们的反应。听起来他们正在考虑将此方法标记为
ThreadSafe

这是因为您的
UIView
正在从一个未创建它的线程进行访问。由于您正在后台线程中创建
NSString
,因此第二个示例可以正常工作,建议使用。(尽管我不认为您应该尝试从后台线程访问
someView.Bounds

我怀疑这也会起作用:

var bounds = someView.Bounds; //UI thread
Task.Factory.StartNew(() => {
    // Outputs expected size data: "{Width=##, Height=##}".
    using (UIView view = new UIView()) {
        SizeF textSize = view.StringSize(someString, someFont, new SizeF(bounds.Width, float.MaxValue), UILineBreakMode.WordWrap);
        Console.WriteLine(textSize);
    }
});
但我会坚持使用
NSString

var bounds = someView.Bounds; //UI thread
Task.Factory.StartNew(() => {
    // Outputs expected size data: "{Width=##, Height=##}".
    using (NSString nssSomeString = new NSString(someString)) {
        SizeF textSize = nssSomeString.StringSize(someFont, new SizeF(bounds.Width, float.MaxValue), UILineBreakMode.WordWrap);
        Console.WriteLine(textSize);
    }
});

StringSize
方法的UI限制确实是不必要的。Xamarin已经做到了

我们已经定义了DrawString(drawInRect:*选择器)可以从 其他线程。StringSize是该功能的一个子集,因此使用它应该是安全的。 未来版本(6.0.10+)将标记为[线程安全]


在您根据更新版本进行编码之前,
NSString
版本将正常工作。

我认为所有UI实现的代码都需要在主线程中运行。在苹果的WWDC视频中,它已经被提到过好几次了。这实际上是这个问题的重点;这实际上与用户界面相关吗?在MonoTouch中,这似乎是指向
NSString
方法的
UIView
快捷方式,不局限于UI线程。我认为这无关紧要,因为在MonoTouch中,您可以通过类似NSString的UIView访问主视图中的内容,它必须有一些指向实际UIView对象的链接。如果在MonoDevelop中的“转到定义”,你可以看到实际的代码(或者至少是它的反编译版本)。也许是时候让我去挖掘它了,这很有趣。我肯定会使用
NSString.StringSize
,我也会同意
Bounds
可能处于“不确定”状态“当在UI线程外点击时,我仍然认为您实际上没有访问
UIView
本身上的任何内容,在这种情况下,这会导致出现问题;您只是在调用一个碰巧属于该类的方法。我会向Xamarin扔一只虫子,看看是否还有其他理由做出选择。不,这不是虫子。不允许从未创建UI对象的线程访问UI对象。这条规则存在于我使用过的每一个UI平台中:iOS、Android、WPF,甚至回到WinForms。我认为语义上的逻辑(“访问”与“操纵”),但我试图证明这一点在这种情况下并不相关。调用
StringSize
并不是以任何方式操纵
UIView
对象,它只是调用一个实用方法(它调用另一个与UI无关的
NSString
实用方法),该方法恰好通过
UIView
实例可用。使用该方法似乎不可能对UI进行任何更改。如果是这样的话,确保它是从UI线程调用将是完全没有必要的。我认为这仍然是苹果定义的:有一节说明这些方法应该只从主线程调用。我相信Xamarin正在检查创建对象的线程,以便为您抛出此异常。
var bounds = someView.Bounds; //UI thread
Task.Factory.StartNew(() => {
    // Outputs expected size data: "{Width=##, Height=##}".
    using (NSString nssSomeString = new NSString(someString)) {
        SizeF textSize = nssSomeString.StringSize(someFont, new SizeF(bounds.Width, float.MaxValue), UILineBreakMode.WordWrap);
        Console.WriteLine(textSize);
    }
});