之前:
然后去使用代码:
class UserRoleItem: NameProtocol { var id:String var name:String init(id:String = "", name:String = "") { self.id = id self.name = name } } enum UserRole:String { case Unknown = "unknown" case SalesConsultant = "sales_consultant" case SalesManager = "sales_manager" case StoreGeneral = "store_general" var name: String { var curName = "" switch self { case .SalesConsultant: curName = "销售顾问" case .SalesManager: curName = "销售经理" case .StoreGeneral: curName = "总经理" default: break } return curName } static var userRoleItemList: [UserRoleItem] { var itemList = [UserRoleItem]() itemList.append(UserRoleItem(id: UserRole.SalesConsultant.rawValue, name: UserRole.SalesConsultant.name)) itemList.append(UserRoleItem(id: UserRole.SalesManager.rawValue, name: UserRole.SalesManager.name)) itemList.append(UserRoleItem(id: UserRole.StoreGeneral.rawValue, name: UserRole.StoreGeneral.name)) return itemList } } self.roleTextField = DropDownTextField(itemList: UserRole.userRoleItemList) protocol NameProtocol { // var name:String { get set } //get set means read and writable var name:String { get } //get means read-only } class DropDownTextField: CommonTextField, UITextFieldDelegate { init(parentVC:UIViewController = UIViewController(), itemList:[NameProtocol] = [NameProtocol](), selectedIndex:Int = Int.InvalidIndex, dropdownRowHeight:CGFloat = DropdwonListRowHeight, didSelectItem:((NameProtocol) -> Void)? = nil) { self.dropdownButton = UIButton() // self.nameList = nameList self.itemList = itemList self.selectedIndex = selectedIndex self.parentVC = parentVC self.dropdownRowHeight = dropdownRowHeight self.didSelectItem = didSelectItem self.selectItemVC = SelectItemViewController() super.init(frame: CGRectZero) |
0x4c43300 <+944>: calll 0x4d8e300 ; function signature specialization <Arg[0] = Exploded, Arg[1] = Exploded, Arg[2] = Dead, Arg[3] = Dead> of Swift._fatalErrorMessage (Swift.StaticString, Swift.StaticString, Swift.StaticString, Swift.UInt) -> () 0x4c43305 <+949>: nopw %cs:(%eax,%eax) 0x4d8e33e <+62>: calll 0x4def640 ; function signature specialization <Arg[0] = Exploded, Arg[1] = Exploded> of Swift.(_fatalErrorMessage (Swift.StaticString, Swift.StaticString, Swift.StaticString, Swift.UInt) -> ()).(closure #2) -> 0x4d8e343 <+67>: ud2 0x4d8e345 <+69>: movl $0x0, -0x14(%ebp) |
log输出:
2016-07-21 21:19:20.158 [Verbose] [main] [EditInfoView.swift:52] init(editMode:) > editMode=Register fatal error: array cannot be bridged from Objective-C |
fatal error: array cannot be bridged from Objective-C
swift fatal error array cannot be bridged from Objective-C
swift 2.2 类型转换小坑 | kaizeiとyimi
Dictionary cannot be bridged from Objective-C -> Issues in Swift · Issue #343 · ccgus/fmdb
“fatal error: array cannot be bridged from Object
Array cannot be bridged from Objective-C | Apple Developer Forums
swift – protocol typed array can’t be downcast to concrete type array – Stack Overflow
1.去试试,加map是否可以:
let mappedUserRoleItemList = UserRole.userRoleItemList.map{$0} self.roleTextField = DropDownTextField(itemList: mappedUserRoleItemList) |
结果,还是会崩溃的:
后来,改为:
let mappedUserRoleItemList = UserRole.userRoleItemList.map({$0 as NameProtocol}) self.roleTextField = DropDownTextField(itemList: mappedUserRoleItemList) |
是可以的。
2.或者是标记为objc:
//protocol NameProtocol { @objc protocol NameProtocol { // var name:String { get set } //get set means read and writable var name:String { get } //get means read-only } |
但是有个问题:
别的实现了该协议的类,枚举中,就要给对应属性,同样的加上@objc,才能通过编译:
class UserRoleItem: NameProtocol { var id:String //var name:String @objc var name:String init(id:String = "", name:String = "") { self.id = id self.name = name } } |
然后这样的确可以继续正常执行代码,不会崩溃。
3.或者是:
学习:
viewController.options = things as [Option]
去加上as试试
//self.roleTextField = DropDownTextField(itemList: UserRole.userRoleItemList) self.roleTextField = DropDownTextField(itemList: UserRole.userRoleItemList as [NameProtocol]) |
结果错误依旧:
别人也去提交了rdar了:
rdar://18878970: App crash. fatal error: array cannot be bridged from Objective-C
protocol typed array can’t be downcast to concrete type array Quick Tips | Codementor
中解释了原因:
此处是定义的协议P,实现了协议的类X
定义的P的数组[P],然后把X的数组[X]赋值给[P],之所以出错,是因为:
So casting an array of protocol objects to an array of its adopter requires bridging. That justifies why @objc fixes the issue:
把一个协议的实现者的数组,强制转换为协议数组,需要桥接bridging
而objc正好可以解决此(桥接)的问题
而深层次的原因是官网文档解释的:
You can check for protocol conformance only if your protocol is marked with the @objc attribute
->但是最新版本的swift 2.2中,却没有了这段提示
然后作者去Playground验证了,结果提示:
To verify that (not that I don’t trust the documentation), I’ve used this code in the playground: protocol P {} class X : P {} let x = X() let y = x is P but I get a different error, stating that: Playground execution failed: <EXPR>:18:11: error: ‘is’ test is always true let y = x is P Writing that in a "regular" project instead we get what expected: protocol P {} class X {} func test() { let x = X() let y = x is P } Cannot downcast from ‘X’ to non-@objc protocol type ‘P’ Conclusion: in order for a protocol typed array to be downcast to a concrete type array, the protocol must be marked with the @objc attribute. The reason is that the runtime uses the is operator to check for protocol conformance, which accordingly to the documentation is only available for bridged protocols. |
[总结]
对于一个协议:
protocol NameProtocol { // var name:String { get set } //get set means read and writable var name:String { get } //get means read-only } |
的数组:
var itemList:[NameProtocol] |
来说,想要将
实现了这个协议的一个类:
class UserRoleItem: NameProtocol { var id:String var name:String init(id:String = "", name:String = "") { self.id = id self.name = name } } |
的数组:
var userRoleItemList: [UserRoleItem] |
赋值给上面的协议的数组:
itemList = userRoleItemList |
就会出现上述错误:
fatal error: array cannot be bridged from Objective-C
对于解决方案,之前别人有提到的:
1.map
let mappedUserRoleItemList = UserRole.userRoleItemList.map{$0} self.roleTextField = DropDownTextField(itemList: mappedUserRoleItemList) |
2.as
self.roleTextField = DropDownTextField(itemList: UserRole.userRoleItemList as [NameProtocol]) |
都不行
解决办法是:
1.还是需要加@objc,即:
把:
protocol NameProtocol { var name:String { get } //get means read-only } |
改为:
@objc protocol NameProtocol { var name:String { get } //get means read-only } |
并且,实现了此协议的类(或枚举)中的属性,也对应的要加上@objc前缀
class UserRoleItem: NameProtocol { var id:String // var name:String @objc var name:String init(id:String = "", name:String = "") { self.id = id self.name = name } } |
才可以。
如果想,实现了对应的协议的类,对应属性不加上@objc,则需要让该类继承自NSObject,即可:
//class UserRoleItem: NameProtocol { class UserRoleItem: NSObject, NameProtocol { var id:String var name:String // @objc var name:String init(id:String = "", name:String = "") { self.id = id self.name = name } } |
2.或者是,用加了as的map:
//此处的UserRole.userRoleItemList是[UserRoleItem] let mappedUserRoleItemList = UserRole.userRoleItemList.map({$0 as NameProtocol}) self.roleTextField = DropDownTextField(itemList: mappedUserRoleItemList) |
也可以。
说明:
1.对于
官网:
The Swift Programming Language (Swift 2.2): Protocols
中的解释,也解释了关于conformance方面的内容
-》
但是最新的swift 2.2中,已经没了:
protocol typed array can’t be downcast to concrete type array Quick Tips | Codementor
中说的:
“You can check for protocol conformance only if your protocol is marked with the @objc attribute”
-》估计是版本变化后,就允许直接downcast了?
-》那为何此处还是出错???
2.根本原因,应该是:
swift – protocol typed array can’t be downcast to concrete type array – Stack Overflow
“Conclusion: in order for a protocol typed array to be downcast to a concrete type array, the protocol must be marked with the @objc attribute. The reason is that the runtime uses the is operator to check for protocol conformance, which accordingly to the documentation is only available for bridged protocols.”
转载请注明:在路上 » [已解决]swift中属性值的协议初始化出错:fatal error array cannot be bridged from Objective-C