15

Update: This is fixed in Xcode 6 beta 6.

The following code causes an EXC_BAD_ACCESS on the delegate?.thing() line:

@class_protocol protocol Fooable {
  func foo()
}

class Bar : Fooable {
  func foo() {
  }
}

weak var delegate: Fooable?

let bar = Bar()
delegate = bar
delegate?.foo()

But everything seems right to me. In order for a variable to be weak, it must have optional type. So the variable delegate is optional. A weak variable's type must also be a class type, so I made the protocol a class protocol. Since I use optional chaining, I expect it to either 1) be nil, and do nothing, or 2) not be nil, and call the method, which should succeed. However, it crashes.

Could it be that optional chaining is not atomic and doesn't retain the expression and the object somehow gets deallocated in between the check for nil and the subsequent call?

Interestingly, if you eliminate the variable bar and assign it directly as delegate = Bar(), the crash goes away. This is really perplexing because assigning an expression to a variable and then assigning the variable and assigning the expression directly should generally behave the same.

user102008
  • 30,736
  • 10
  • 83
  • 104
  • 5
    not surprisd if this is another Swift bug – Bryan Chen Jun 11 '14 at 23:49
  • I had a play with it, and this only happens when it's both weak *and* of a protocol type (i.e. changing the declaration to either of `weak var delegate: Bar?` or `var delegate: Fooable?` fixes this). Definitely looks like a bug. – Greg Jun 13 '14 at 09:37
  • I experienced the same issue on XCode 6.1. When I ran it in debug mode it is fine. But happened in release mode. – yibuyiqu Nov 06 '14 at 23:13
  • possible duplicate of [How can I make a weak protocol reference in 'pure' Swift (w/o @objc)](http://stackoverflow.com/questions/24066304/how-can-i-make-a-weak-protocol-reference-in-pure-swift-w-o-objc) – Saqib Saud Feb 08 '15 at 10:16

2 Answers2

4

I suspect the reason weak var delegate: Fooable?is not working is because that line of code, which is using optional chaining, is checking for protocol conformance.

According to Apple Swift Programming manual:

“Even if you are not interoperating with Objective-C, you need to mark your protocols with the @objc attribute if you want to be able to check for protocol conformance.”

If you substitute @class_protocol with @objc it should not crash. Also as per the manual, using @objc only allows the protocol to be adopted by classes (no structs or enums conformance).

Raz
  • 1,387
  • 12
  • 13
0

Like @PartiallyFinite, I had to play with the code a little too. Apple's iBook on Swift has a blurb about this that may help (with a protocol named ExampleProtocol and a class SimpleClass that conforms to that protocol): "Even though the variable protocolValue has a runtime type of SimpleClass, the compiler treats it as the given type of ExampleProtocol. This means that you can’t accidentally access methods or properties that the class implements in addition to its protocol conformance.”

That said, you should be able to call foo() on your delegate (barring it doesn't go away before calling it), but just remember the delegate here is declared to be of type Fooable?, not Bar, under the hood. This may be a bug, but I got it to work by entering:

weak var delegate: Bar? = bar
delegate?.foo()
BJ Miller
  • 1,536
  • 12
  • 16