最新消息:20210816 当前crifan.com域名已被污染,为防止失联,请关注(页面右下角的)公众号

[已解决]swift中属性值的协议初始化出错:fatal error array cannot be bridged from Objective-C

iOS crifan 2151浏览 0评论

之前:

[已解决]swift中实现包含某个属性的协议

然后去使用代码:

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

&quot;fatal error: array cannot be bridged from Objective-C&quot;—Why are you even trying, Swift? (Swift) – Codedump.io

ios – "fatal error: array cannot be bridged from Objective-C"—Why are you even trying, Swift? – Stack Overflow

“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

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
83 queries in 0.178 seconds, using 22.13MB memory