My question is in regards to an error that I kept on seeing while writing a function to initialize an optional array in a struct that I solved by just changing the struct to the class. I am hoping that someone can explain to me what I am not understanding about structs and classes that is causing this problem. Here is my code.
struct DataStorage {
//When I change this to class DataStorage this works
var listOfVariables = [VariableType]
var allDataPoints: [[DataPoint]]?
init() {
listOfVariables = VariableType.getAllVariables(managedObjectContext)
}
func initializeAllDataPoints() {
//THIS IS THE LINE IN QUESTION
allDataPoints = [[DataPoint]](count: listOfVariables.count, repeatedValue: [DataPoint]())
}
}
So, the function initializeAllDataPoints is what is causing the error and that is truly the relevant part of this question. The error that I get is Cannot assign to 'allDataPoints' in 'self'. I only get this error when DataStorage is a struct and I don't get it when DataStorage is a class. What am I not understanding about classes and structs that is causing this difference in behavior?
Whenever a method in a struct modifies one of its own properties, you have to use the mutating keyword.
I believe that if you write:
mutating func intializeAllDataPoints() { ... }
it should work for you.
This article gives a little more background information.
Related
I'm relatively new to Swift, so I hope I'm not asking a stupid question.
I have some code that instantiates an array of type Error, which will later be iterated and printed to the console. When running this code through Instruments using the "Leaks" instrument, it shows a leak of _SwiftNativeNSError. If I change the array type from [Error] to [Any], the leak disappears, even though it is still actually holding an object conforming to Error. The leak is not reproducible with any other data types or protocols that I've tried.
Here's some sample code:
class myLeak {
lazy var errors = [Error]()
enum err: Error {
case myFirstError
}
func doSomething() {
errors.append(err.myFirstError)
for error in errors {
print(String(describing: error))
}
}
}
// call with let myleak = myLeak(); myleak.doSomething()
Calling the doSomething() function immediately creates a leak. Switching [Error]() to [Any]() resolves the leak, but I'm not happy with this as a solution without understanding the underlying problem. The problem is also solved by changing [Error]() to my enum implementing the Error protocol: [err](). I've also tried creating my own custom protocol just to prove if this is being caused specifically by Error, and I'm only able to reproduce the problem when using Error; my own custom protocol did not exhibit this behaviour.
Originally, my code used a forEach loop to iterate the array, but I then tried re-writing it to use a standard for loop in case the closure in forEach was causing the issue, but this didn't work.
I'm suspicious that this may be a Swift bug (in which case, I will open an issue for it), but there's also a chance that I'm missing a key piece of understanding. If what I'm doing is bad practice, I'd like to understand why.
Update:
After speaking with Joe Groff, an Apple engineer, this is the bug you could have encountered: https://bugs.swift.org/browse/SR-6536
Original Answer
I've played a bit with your code and I think the problem is due to Error type.
In fact, taking the code by Josh, you can find a different behaviour if you use Error or MyError as the type of your array.
I guess the problem arises since the deinit call is not forwarded to CustomObject since Error is just a protocol and it's not aware of the underlying class. While, MyError is. We can wait for other people to have clarifications on this behaviour.
Just for simplicity, I'm using a Playground here. See that I'm not even trying to print the error value.
import UIKit
class ViewController: UIViewController {
var errors: [Error] = [] // change to MyError to see it working
enum MyError: Error {
case test (CustomObject)
}
class CustomObject {
deinit {
print("deiniting")
}
}
override func viewDidLoad() {
super.viewDidLoad()
let testerror = MyError.test(CustomObject())
errors.append(testerror)
errors.removeAll()
}
}
do {
let viewController = ViewController()
// just for test purposes ;)
viewController.viewDidLoad()
}
I tested your code and it looks like the String(describing) statement is causing the string to retain the error, which is just weird. Here is how I can tell: I created an associated object that prints out when its being deinitialized.
import UIKit
class ViewController: UIViewController {
var errors = [Error]()
override func viewDidLoad() {
super.viewDidLoad()
class CustomObject {
deinit {
print("deiniting")
}
}
enum MyError: Error {
case test (CustomObject)
}
let testerror = MyError.test(CustomObject())
errors.append(testerror)
for error in errors {
//print(String(describing: error))
}
errors.removeAll()
}
}
When the print doesn't run, sure enought he associated object is deinitialized at when the error is removed from the array and the output is:
deiniting
Uncomment the print and the output becomes:
test(CustomObject #1 in stack.ViewController.viewDidLoad() -> ())
At first I thought it was the print that's the problem but If I refactor to:
errors.forEach{print($0)}
I get the output:
test(CustomObject #1 in stack.ViewController.viewDidLoad() -> ())
deiniting
But if I change it to:
errors.map {String(describing:$0)}.forEach{print($0)}
Then deinit is no longer called:
test(CustomObject #1 in stack.ViewController.viewDidLoad() -> ())
Weirdness. Maybe file a radar?
This bug was Fixed in Xcode 9.3.
I have a piece of code:
protocol ModuleOutput: class {
var output: Any! { get }
}
class SomeClass {
var output: Any!
init() {
self.output = "ervwe"
}
func getIt() {
let r = (self as! ModuleOutput).output
}
}
And when code is running ,
I get an error:
exc_bad_instruction (code=exc_i386_invop subcode=0x0)
in this line:
(self as! ModuleOutput).output
What's my mistake?
totally agreed with Hamish and vadian here. ObjC casting and Swift as? are very different operations that achieve completely different things. In Swift, you must declare conformances.
The (more) correct code would look like this:
protocol ModuleOutput: class {
var output: Any { get }
}
class SomeClass: ModuleOutput {
var output: Any = "ervwe"
func getIt() {
let result = output
// ... use result
}
}
This is still likely wrong, because output should almost certainly not be Any (that's almost never the right type), but it's closer.
I don't quite understand your comments below, but you seem to suggest you have a class that informally conforms to TranditionalViewWithOutput, but does not formally conform in ObjC. That used to be very common in ObjC (prior to v2, when #optional was added), so it wouldn't be surprising in older code. But it's not a problem. Just conform it:
extension InformallyConformingVC: TranditionalViewWithOutput {}
If it already conforms syntactically, that just tells the compiler that it fully conforms. (Swift protocols are not just bags of syntax.)
I'm not clear that this is really your question, but it's the best I can make out from the comments. Perhaps you should reword the question closer to your actual intent.
Coming from .Net, I am trying to learn Swift3/iOS and got puzzled by the following apparent inconsistent behaviour of optional protocol members. I suspect its something got to do with the juggling between objc/swift words, but what am I missing here actually?
// In playground, given below:
#objc protocol SomePtotocol {
#objc optional func someMethod()
}
class SomeDelegate: NSObject, SomePtotocol {
}
class SomeController: NSObject {
var delegate: SomePtotocol = SomeDelegate()
}
// This works and compiles without error
let controller = SomeController()
controller.delegate.someMethod?() // No error, typed as '(() -> ())?'
// But this fails to even compile ??
let delegate = SomeDelegate()
delegate.someMethod?() // Error: 'SomeDelegate' has no member 'someMethod'
I would expect either both to fail or both pass, so if someone could please enlighten me on this anomaly.
The difference between the two blocks of code is the type of the variable involved.
In the first block, delegate is explicitly typed as SomePtotocol, and this protocol defines the someMethod method, so your statement is valid.
In the second block, delegate is implicitly typed as SomeDelegate and although this class conforms to SomePtotocol, it doesn't implement the optional method someMethod, so you get an error.
If you change your second block to
let delegate: SomePtotocol = SomeDelegate()
delegate.someMethod?()
which is equivalent to the first block, then there is no error.
I'm trying to implement a simple multi-delegate situation:
protocol Subscribable: class {
associatedtype Subscriber: AnyObject
var subscribers: NSHashTable<Subscriber> { get }
}
protocol ControllerSubscriber: class {
func controllerDidSomething()
}
class Controller: Subscribable {
typealias Subscriber = ControllerSubscriber
var subscribers = NSHashTable<Subscriber>.weakObjects() // Error
}
Error: Using 'ControllerSubscriber' as a concrete type conforming to protocol 'AnyObject' is not supported.
My question is:
What does this error mean exactly?
What are the underlying concepts the thing I'm trying to do fails for?
Why is this "not supported"?
And of course, how do I work around this? In the sense of an actual solution not a work around.
I have such a hard time understanding Swift's generics system. I seem to be running into seemingly simple situations like this constantly. I just want to put a thing conforming to a protocol into another thing :( . I would like to know where my thinking goes wrong so I can fix it and never have to see these errors again.
There is this related question but please note the answers give only workarounds, no explanations or solutions.
It is probably not fair to blame this problem on Swift. Reasoning about types seems to be some meta-art we will first have to get used to (unless you have been sitting on the C++ standards committee for the last 30 years that is :-).
Turns out your problem is related to your choice of NSHashTable as the data structure to hold your subscribers. The following would compile with minimal changes:
protocol Subscribable: class {
associatedtype Subscriber
var subscribers: [Subscriber?] { get }
}
protocol ControllerSubscriber: class {
func controllerDidSomething()
}
class Controller: Subscribable {
typealias Subscriber = ControllerSubscriber
var subscribers = [Subscriber?]()
}
however, it lacks the weak semantics and is not really useful yet. The list of subscribers is exhibited as a property and has to be manipulated directly by the clients. Furthermore each implementation of Subscribable has to implement its own notification mechanism and there is hardly any logic that is centralised by this approach. Technically you can use it like this:
class Controller: Subscribable {
typealias Subscriber = ControllerSubscriber
var subscribers = [Subscriber?]()
func notify() {
for case let subscriber? in subscribers {
subscriber.controllerDidSomething()
}
}
}
var controller = Controller()
class IWillSubscribe : ControllerSubscriber {
func controllerDidSomething() {
print("I got something")
}
}
controller.subscribers.append(IWillSubscribe())
controller.notify()
but that is neither very practical nor very readable. This would have been an acceptable solution (since it was the only one) up to Java 7, but even in Java 8 (and much more so in Swift) we would like to encapsulate the notification logic into the Subscribable protocol as a default implementation, but that would be another post.
Since you chose to implement subscribers as an NSHashTable (there is probably an ARC reason to desire weak references here) there seems to be some Objective-C trickery involved. After much experimentation (and finally finding the fourth answer to this question I got the following to work:
protocol Subscribable: class {
associatedtype Subscriber : AnyObject
var subscribers: NSHashTable<Subscriber> { get }
}
#objc protocol ControllerSubscriber: class {
func controllerDidSomething()
}
class Controller: Subscribable {
typealias Subscriber = ControllerSubscriber
var subscribers = NSHashTable<Subscriber>.weakObjects()
func notify() {
for subscriber in subscribers.allObjects {
subscriber.controllerDidSomething()
}
}
}
var controller = Controller()
class IWillSubscribe : ControllerSubscriber {
func controllerDidSomething() {
print("I got something")
}
}
let iDoSubscribe = IWillSubscribe()
controller.subscribers.add(iDoSubscribe)
controller.notify()
which is virtually identical to your original (with some proof around it). As it seems Objective-C #protocols are not quite the same as Swift protocols, but Swift can actually do either.
There is quite a lot of subtlety in this though, only allObjects works without type erasure, your trusty objectEnumerator only returns Any? and that is a stupid animal to get anything from. Also note that
let iDoSubscribe = IWillSubscribe()
is instrumental. At first I tried
controller.subscribers.add(IWillSubscribe())
which actually added something to the count of subscribers, but went away with any attempt to iterate (as one should expect from a weak reference that is not referred to anywhere else).
A very late answer that is already way too long, just to prove that this is still an issue, even with Swift 3. Maybe this will get better once this Jira ticket is resolved.
I have come across this problem several times now, without ever finding the cause.
When attempting to access a property of ClassY in ClassX I get an EXC_BAD_ACCESS error. This happens even if I instantiate a clean copy and attempt to access the property immediately:
let classY = ClassY()
println(classY.someTextProperty)
EXC_BAD_ACCESS
As far as I am aware this error should only be caused by trying to access a deallocated instance, which presumably cannot be the case in the above code.
Where it gets strange, is that if I declare ClassY in the same file as ClassX then the problem disappears. (ClassY does have some private properties, but I am not trying to access these and anyway, making them all public does not solve the problem)
I am not including the source code here since there is more of it than is practical to isolate. I have not been able to reproduce this reliably in a standalone project because I am unable to find any kind of pattern.
Has anyone come across a similar kind of problem, and does anyone have an idea for a solution (obviously I can include the definition in the same file, but that is a workaround rather than a solution)?
I am currently running iOS 8.3 / Swift 1.2 but had this problem already in iOS 8.1.
UPDATE
Following lots of trial and error, it seems that the problem comes from lazy properties and protocol conformance. Again this is not something I can reproduce in a plain playground (perhaps the declarations need to be in different files).
My ClassY conforms to ProtocolY which has several properties:
protocol ProtocolY {
var number: Int { get }
var text: String { get }
}
When ClassY conforms to ProtocolY, if it implements either of the properties as a lazy property then accessing a property causes the EXC_BAD_ACCESS crash. If ClassY is not marked as conforming to ProtocolY or if none of the properties declared in ProtocolY as implemented by ClassY are lazy properties then no crash occurs.
Obviously this is just narrowing down but still does not give me an explanation. I know of no reason why implementations of protocol properties should not be lazy.
UPDATE Here is an example of properties on ClassY:
private(set) lazy var currentPage: Int = {
let subject: RACReplaySubject = RACReplaySubject(capacity: 1)
subject.sendNext(self.currentPage)
return subject
}()
var currentPage: Int {
didSet {
(self.currentPageSignal as? RACSubject)?.sendNext(self.currentPage)
}
}
currentPage is set in the init method.
If I use instead:
private(set) var currentPage: Int = {
let subject: RACReplaySubject = RACReplaySubject(capacity: 1)
return subject
}()
var currentPage: Int {
didSet {
(self.currentPageSignal as? RACSubject)?.sendNext(self.currentPage)
}
}
(and do a sendNext in the init method) then everything works fine.
I'm fairly confident that the fact that I'm using ReactiveCocoa is irrelevant - I have also tried lazy properties which are simple Strings, with the same result.