Dou*_*ead 12 optional ios swift
我是UIStoryboardSegue的子类,每当我尝试使用两个UIViews中的一个时,Xcode就会让我添加两个可选的unwraps(!!),例如:
let sourceView = self.sourceViewController.view
sourceView!!.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight
Run Code Online (Sandbox Code Playgroud)
要么
let sourceView = self.sourceViewController.view!
sourceView!.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight
Run Code Online (Sandbox Code Playgroud)
要么
self.sourceViewController.view!!.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight
Run Code Online (Sandbox Code Playgroud)
我想知道是否有人可以解释为什么会这样.
该sourceViewController物业UIStoryboardSegue的类型为AnyObject,并作为一个Objective-C兼容的功能,一旦你import Foundation,事情得到一个有点怪异AnyObject.
AnyObjectSwift不是在类型上寻找方法,而是寻找Objective-C选择器,就像Objective-C对id类型一样.任何类中的任何选择器都是公平的游戏:如果你愿意,你可以尝试调用activeProcessorCount你的对象,即使那是一个NSProcessInfo选择器,编译器会让你这样做.(由于显而易见的原因,它会在运行时失败.)这称为动态调度,与静态调度(Swift中的正常调用机制)相反.
但是,关于动态调度的一件事是它总是添加一层隐式包装.如果你有一个返回a的Objective-C属性String,动态调度将返回一个String!.
当多个类声明具有相同名称但不同返回类型的选择器(或具有不同的参数,但我们对此情况不感兴趣)时,事情会变得多毛.我不知道编译器如何从它知道的许多同名的选择器中选择哪个选择器.无论哪种方式,它都会选择一个,除非你将对象转换为更精确的类型,否则你会坚持使用它,在这种情况下,Swift编译器只允许你使用静态调度.
使用swiftc's -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)))))
Run Code Online (Sandbox Code Playgroud)
有很多瑕疵,但你可以看到它产生了一个dynamic_member_ref_expr而不是一个member_ref_expr.如果你一直向右滚动到decl"属性",你会看到它正在使用UIGestureRecognizer的view属性(声明为UIView?),因此表达式返回一个UIView?!.
相比之下,UIViewController的view属性被声明为UIView!.如果编译器选择了这个选择器,那么你最终会得到一个UIView!!,而你只需要一个级别的显式解包.
您可以(并且必须在此类实例中)显式解包隐式未展开的值.用sourceView作为UIView?!,第一!解包UIView?!入UIView?,而第二个解包UIView?成最终可用UIView.这就是你需要两个惊叹号的原因.
声明用于动态分派的选择器的类是无关紧要的,只要目标对象实现它,接受兼容的参数,并返回兼容的类型.UIView?并且UIView!在二进制级别兼容,所以最后,您的程序仍然运行.但是,如果你以某种方式期待一种String或另一种不相关的类型,你可能会感到惊讶.在我看来,你应该尽可能地避免动态调度.
tl; dr:如果你转换sourceViewController为UIViewController,你得到正确的view属性定义,根本不需要打开它.
| 归档时间: |
|
| 查看次数: |
745 次 |
| 最近记录: |