Swift 类型转换怎么做?is、as、as?、as! 有什么区别?
Swift 用 is、as、as?、as! 四个操作符做类型检查和转换。is 检查"是不是这个类型",返回 Bool;as 向上转型(子类→父类),永远成功,编译器自动推断时甚至不用写;as? 向下转型(父类→子类),成功返回可选值,失败返回 nil,安全;as! 强制向下转型,失败直接 crash,危险。
核心规则:向上转型用 as(安全,编译器保证),向下转型优先 as?(安全,失败返回 nil),只在逻辑上100%确定时才用 as!。实际开发中 as? 用得最多——处理 JSON 解析、UITableViewCell 复用、协议类型的向下转型,几乎都是 as?。
Any 和 AnyObject 是 Swift 的两种"万能类型":Any 可以表示任何类型(包括值类型和引用类型),AnyObject 只能表示类类型。类型转换在处理 [Any] 数组时最常见——网络请求返回的 JSON 经常被解析为 [String: Any],需要逐个 as? 转换。
追问
as、as?、as! 分别在什么场景用?
as:向上转型(子类→父类)和桥接 Objective-C 类型(String as NSString),编译器保证安全。as?:向下转型不确定是否成功时,比如 JSON 解析 value as? String、UITableViewCell 复用 cell as? CustomCell,日常开发最常用。as!:逻辑上100%确定类型时,比如 storyboard 里已知 cell 类型,或者单元测试中断言类型。和 try! 一样,as! 基本只在"失败就是 bug"的场景用。
Any 和 AnyObject 有什么区别?
Any 可以表示任何类型——class、struct、enum、闭包、元组都行。AnyObject 只能表示类类型,本质是 Objective-C 的 id 类型。从 Objective-C 桥接过来的 API 返回 AnyObject,纯 Swift 代码用 Any。实际项目中尽量少用这两个——频繁用 Any 说明设计有问题,应该用协议或泛型代替。
protocol 类型的 as? 转型有什么坑?
把协议类型向下转型为具体类型时,只有协议标记了 @objc 或者是 class-only 协议才能 as?。纯 Swift 协议(没有 class 约束)的 existential 不能直接 as? 转为具体类型——编译器会报错。解法:把协议改为 @objc protocol,或者用泛型/关联类型替代类型转换。
为什么 Swift 不推荐频繁用类型转换?
频繁用 is/as? 说明你在绕过类型系统,通常意味着设计有问题。正确做法是用多态——定义协议方法让子类各自实现,调用方不需要知道具体类型。如果代码里一堆 if let x = y as? A { ... } else if let x = y as? B { ... },应该重构为协议 + 多态调用。
写段代码
swiftclass MediaItem { var name: String; init(name: String) { self.name = name } } class Movie: MediaItem { var director: String; init(name: String, director: String) { self.director = director; super.init(name: name) } } class Song: MediaItem { var artist: String; init(name: String, artist: String) { self.artist = artist; super.init(name: name) } } let library: [MediaItem] = [ Movie(name: "Casablanca", director: "Curtiz"), Song(name: "Blue Suede Shoes", artist: "Elvis"), Movie(name: "Citizen Kane", director: "Welles") ] // as? 安全转型 for item in library { if let movie = item as? Movie { print("Movie: \(movie.name) by \(movie.director)") } else if let song = item as? Song { print("Song: \(song.name) by \(song.artist)") } } // switch + is/as for item in library { switch item { case is Movie: print("\(item.name) is a movie") case let song as Song: print("\(song.name) by \(song.artist)") default: break } }