草庐IT

ios - Xcode 强制 Swift 可选解包两次 (!!)

coder 2023-07-16 原文

我正在对 UIStoryboardSegue 进行子类化,每次我尝试使用两个 UIView 之一时,Xcode 都会让我添加两个可选的解包 (!!),例如:

let sourceView = self.sourceViewController.view
sourceView!!.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight

let sourceView = self.sourceViewController.view!
sourceView!.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight

self.sourceViewController.view!!.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight

我想知道是否有人可以解释这是为什么。

最佳答案

UIStoryboardSeguesourceViewController 属性类型为 AnyObject,并且作为 Objective-C 兼容性功能,一旦您 import Foundation ,使用 AnyObject 会有些奇怪。

Swift 不是寻找 AnyObject 类型的方法,而是寻找 Objective-C 选择器,就像 Objective-C 寻找 id 类型一样。来自任何类的任何选择器都是公平游戏:如果你愿意,你可以尝试在你的对象上调用 activeProcessorCount,即使那是一个 NSProcessInfo 选择器,编译器会让你做吧。 (由于显而易见的原因,它会在运行时失败。)这称为动态分派(dispatch),与静态分派(dispatch)(Swift 中的正常调用机制)相反。

不过,关于动态调度的一件事是它总是添加一层隐式包装。如果您有一个返回 String 的 Objective-C 属性,动态调度将让它返回一个 String!

当多个类声明具有相同名称但返回类型不同(或具有不同参数,但我们对这里的这种情况不感兴趣)的选择器时,事情就会变得棘手。我不知道编译器如何从它知道的许多同名选择器中选择哪个选择器。无论哪种方式,它都会选择一个,除非您将对象转换为更精确的类型,否则您将坚持使用它,在这种情况下,Swift 编译器将只允许您使用静态分派(dispatch)。

使用 swiftc-dump-ast 参数,我们可以看到编译器如何解析表达式的类似 lisp 的表示:

(pattern_binding_decl
  (pattern_named type='UIView?!' 'sourceView')
  (dynamic_member_ref_expr type='UIView?!' location=MySegue.swift:15:46 range=[MySegue.swift:15:25 - line:15:46] decl=UIKit.(file).UIGestureRecognizer.view
    (member_ref_expr type='AnyObject' location=MySegue.swift:15:25 range=[MySegue.swift:15:25 - line:15:25] decl=UIKit.(file).UIStoryboardSegue.sourceViewController
      (derived_to_base_expr implicit type='UIStoryboardSegue' location=MySegue.swift:15:20 range=[MySegue.swift:15:20 - line:15:20]
        (declref_expr type='Segue' location=MySegue.swift:15:20 range=[MySegue.swift:15:20 - line:15:20] decl=xxx.(file).Segue.func decl.self@MySegue.swift:14:7 specialized=no)))))

有很多麻烦,但您可以看到它生成了 dynamic_member_ref_expr 而不是 member_ref_expr。如果一直向右滚动到 decl“属性”,您会看到它正在使用 UIGestureRecognizerview 属性( declared as UIView? ),以便表达式返回 UIView?!

相比之下,UIViewControllerview 属性被声明为UIView!。如果编译器改为选择此选择器,您将得到一个 UIView!!,并且您将只需要一个级别的显式展开。

您可以(并且必须,在这种情况下)显式解包隐式解包的值。将 sourceView 作为 UIView?!,第一个 !UIView?! 解包为 UIView? ,第二个将 UIView? 解包为最终可用的 UIView。这就是为什么您需要两个感叹号。

声明用于动态调度的选择器的类是无关紧要的,只要目标对象实现它,接受兼容的参数,并返回兼容的类型。 UIView?UIView! 在二进制级别是兼容的,所以最终,您的程序仍然可以运行。但是,如果您以某种方式期待 String 或其他不相关的类型,您可能会大吃一惊。在我看来,您应该尽可能避免动态调度。

tl;dr:如果您将 sourceViewController 转换为 UIViewController,您将获得正确的 view 属性定义和根本不需要打开它。

关于ios - Xcode 强制 Swift 可选解包两次 (!!),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29932361/

有关ios - Xcode 强制 Swift 可选解包两次 (!!)的更多相关文章

  1. ruby - 如何在 Lion 上安装 Xcode 4.6,需要用 RVM 升级 ruby - 2

    我实际上是在尝试使用RVM在我的OSX10.7.5上更新ruby,并在输入以下命令后:rvminstallruby我得到了以下回复:Searchingforbinaryrubies,thismighttakesometime.Checkingrequirementsforosx.Installingrequirementsforosx.Updatingsystem.......Errorrunning'requirements_osx_brew_update_systemruby-2.0.0-p247',pleaseread/Users/username/.rvm/log/138121

  2. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  3. Ruby 文件 IO 定界符? - 2

    我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

  4. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

  5. ruby-on-rails - Rake 任务仅调用一次时执行两次 - 2

    我写了一个非常简单的rake任务来尝试找到这个问题的根源。namespace:foodotaskbar::environmentdoputs'RUNNING'endend当在控制台中执行rakefoo:bar时,输出为:RUNNINGRUNNING当我执行任何rake任务时会发生这种情况。有没有人遇到过这样的事情?编辑上面的rake任务就是写在那个.rake文件中的所有内容。这是当前正在使用的Rakefile。requireFile.expand_path('../config/application',__FILE__)OurApp::Application.load_tasks这里

  6. ruby - 为什么不能使用类IO的实例方法noecho? - 2

    print"Enteryourpassword:"pass=STDIN.noecho(&:gets)puts"Yourpasswordis#{pass}!"输出:Enteryourpassword:input.rb:2:in`':undefinedmethod`noecho'for#>(NoMethodError) 最佳答案 一开始require'io/console'后来的Ruby1.9.3 关于ruby-为什么不能使用类IO的实例方法noecho?,我们在StackOverflow上

  7. ruby - 如何在 Cucumber 步骤定义中使单词可选? - 2

    我在下面有一个步骤定义,它执行我想要它执行的操作,即它根据“PAGES”哈希的“page”元素检查页面的url。Then(/^Ishould(still)?beatthe"(.*)"page$/)do|still,page|BROWSER.url.should==PAGES[page]end步骤定义用于两者我应该在...页面我应该还在...页面但是,我不需要将“still”传递到block中。我只需要它是可选的以匹配步骤但不传递到block中。我该怎么做?谢谢。 最佳答案 您想将“静止”组标记为非捕获。这是通过使用?:启动组来完成的

  8. ruby - 强制浏览器下载文件而不是打开文件 - 2

    我要下载http://foobar.com/song.mp3作为song.mp3,而不是让Chrome在其native中打开它浏览器中的播放器。我怎样才能做到这一点? 最佳答案 您只需要确保发送这些header:Content-Disposition:attachment;filename=song.mp3;Content-Type:application/octet-streamContent-Transfer-Encoding:binarysend_file方法为您完成:get'/:file'do|file|file=File.

  9. ruby-on-rails - 多次选择一个随机数,但绝不会两次选择相同的随机数 - 2

    这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:HowdoIgeneratealistofnuniquerandomnumbersinRuby?我想做的事:Random.rand(0..10).timesdoputsRandom.rand(0..10)end但如果随机数已经显示过,则无法再次显示。如何最轻松地做到这一点?

  10. ruby - 强制 Ruby 不以标准形式/科学记数法/指数记数法输出 float - 2

    我遇到了同样的问题here对于python,但对于ruby​​。我需要输出这样一个小数字:0.00001,而不是1e-5。有关我的特定问题的更多信息,我正在使用f.write("Mynumber:"+small_number.to_s+"\n")输出到一个文件对于我的问题,准确性不是什么大问题,所以只做一个if语句来检查是否small_number那么更通用的方法是什么? 最佳答案 f.printf"Mynumber:%.5f\n",small_number您可以将.5(小数点右侧5位数字)替换为您喜欢的任何特定格式大小,例如,%8

随机推荐