This is my first time implementing a singleton to share an instance of an object in swift. Everything seems to be working totally fine, except for when I try and add an element to an array (accessing from another class) that lives within my singleton object. It indeed does not append any objects to the array at all. I'm thinking that it's appending onto an array, but not the same instance of the class that I would like it to be (as I only want one and only one instance). However, If I append elements onto the array from the init() of the class, everything works out just fine. Here's some code (I've simplified all the classes to make things more obvious):
File 1:
class Brew: NSObject {
var method = Method()
//Singleton variable
private static var currentBrew: Brew?
//Method to get the current (and only) brew object
static func getCurrentBrew() -> Brew {
if currentBrew == nil {
currentBrew = Brew()
}
return currentBrew!
}
}
struct Method {
var chemex = Device()
init() {
//If I append here - everything works fine
//chemex.instructions.append = (Instruction(title: "Prepare", direction: "Prewet & Heat", time: 3, water: 0))
}
}
struct Device {
var instructions = [Instruction]()
init() {
instructions.append(Instruction(title: "None", direction: "None", time: 1, water: 0, index: 0))
}
File 2: (where I would like to append to the array of instructions)
let brew = Brew.getCurrentBrew() //How i'm accessing the object
//I'm calling this method from viewDidLoad to set up the array
func setupBrewDevices() {
//This is the line that does not actually append to the singleton instance
brew.method.chemex.instructions.append(Instruction(title: "Extraction", direction: "Match water.", time: 8 , water: 25))
Just a side note, I also tried to make a method that would append an instruction onto the array that lives inside of the same class, but that had the same result. Hopefully this is clear enough - I appreciate any help!
Thanks,
Cole
There is a better way to create a singleton instance in Swift.
class Brew: NSObject {
static let currentBrew = Brew()
var method = Method()
}
This is thread-safe and avoids using an optional.
That said, when I tried your code the instructions array ended up with two elements like I would expect ("None") and ("Extraction"). The problem may lie elsewhere in your code.
Related
I'm building a Kotlin library to use in my iOS app using Kotlin/Native. After I call some methods in the library from Swift, which works, I also want to call methods in Swift from the library. To accomplish this I implemented an interface in the library:
class Outbound {
interface HostInterfaceForTracking {
fun calcFeatureVector(bitmap: Any?): Array<Array<FloatArray>>?
}
var hostInterface: HostInterfaceForTracking? = null
fun registerInterface(hostInterface: HostInterfaceForTracking) {
this.hostInterface = hostInterface
instance.hostInterface = hostInterface
}
}
This is implemented on the Swift side like this:
class HostInterfaceForTracking : OutboundHostInterfaceForTracking {
var t : Outbound? = nil
init() {
TrackingWrapper.instance?.runOnMatchingLibraryThread {
self.t = Outbound()
self.t!.registerInterface(hostInterface: self)
}
}
func calcFeatureVector(bitmap: Any?) -> KotlinArray<KotlinArray<KotlinFloatArray>>? {
do {
var test : Any? = (bitmap as! Bitmap).bitmap
return nil
} catch {
return nil
}
}
}
The TrackingWrapper looks like this:
class TrackingWrapper : NSObject {
static var instance: TrackingWrapper? = nil
var inbound: Inbound? = nil
var worker: Worker
override init() {
self.worker = Worker()
super.init()
initInboundInterface()
}
func initInboundInterface() {
runOnMatchingLibraryThread {
TrackingWrapper.instance = self
self.inbound = Inbound()
HostInterfaceForTracking()
}
}
func runOnMatchingLibraryThread(block: #escaping() -> Void) {
worker.enqueue {
block()
}
}
}
The function runOnMatchingLibraryThread is needed because every call to the TrackingLibrary needs to be called from the exact same thread, so the Worker class initializes a thread and enqueues every method to that thread.
The Bitmap in this case is simply a wrapper for an UIImage, which I already accessed with the .bitmap call, so I've tried to access the wrapped UIImage and save it in the test variable. The library gets the current camera frame from the Swift side every few frames and sends the current image wrapped as a Bitmap to the method calcFeatureVector depicted here.
Problem: My memory load starts increasing as soon as the app starts until the point it crashes. This is not the case if I don't access the wrapped UIImage (var test : Any? = (bitmap as! Bitmap)). So there is a huge memory leak, just by accessing the wrapped variable on the Swift side. Is there anything I've missed or is there any way to release the memory?
Looks like you have a circular dependency here:
TrackingWrapper.instance?.runOnMatchingLibraryThread {
self.t = Outbound()
self.t!.registerInterface(hostInterface: self)
}
You are asking a property inside HostInterfaceForTracking to maintain a strong reference to the same instance of HostInterfaceForTracking. You should be using [weak self] to avoid the circular reference.
EDIT:
Ok after seeing the rest of you code theres a lot to unpack. There is a lot of unnecessary bouncing back and forth between classes, functions and threads.
There is no need to use runOnMatchingLibraryThread to just create an instance of something. You only need to use that for the code processing the image itself (I would assume, I haven't seen anything so far that requires being split off into another thread). Inside TrackingWrapper, you can create a singleton more easily, and matching the swift pattern by simply doing this as the first line:
static let shared = TrackingWrapper()
And everywhere you want to use it, you can just call TrackingWrapper.shared. This is more common and will avoid one of the levels of indirection in the code.
I'm not sure what Worker or Inbound are, but again these can and should be created inside the TrackingWrapper init, rather than branching Inbound's init, to use another thread.
Inside initInboundInterface you are creating an instance of HostInterfaceForTracking() which doesn't get stored anywhere. The only reason HostInterfaceForTracking is continuing to stay in memory after its creation, is because of the internal circular dependency inside it. This is 100% causing some form of a memory issue for you. This should probably also be a property on TrackingWrapper, and again, its Init should not be called inside runOnMatchingLibraryThread.
Having HostInterfaceForTracking's init, also using runOnMatchingLibraryThread is problematic. If we inline all the code whats happening is this:
TrackingWrapper
init() {
self.runOnMatchingLibraryThread {
TrackingWrapper.instance = self
self.inbound = Inbound()
TrackingWrapper.instance?.runOnMatchingLibraryThread {
self.t = Outbound()
self.t!.registerInterface(hostInterface: self)
}
}
}
Having all these classes unnecessarily keep coming back to TrackingWrapper is going to cause issues.
Inside HostInterfaceForTracking 's init, no need to be creating Outbound on a separate thread. First line in this class can simply be:
var t : Outbound = OutBound()
Or do it in the init if you prefer. Either way will also remove the issue of needing to unwrap Outbound before using it.
Inside Outbound you are storing 2 references to the hostInterface instance:
this.hostInterface = hostInterface
instance.hostInterface = hostInterface
I would have imagined there should only be 1. If there are now multiple copies of a class that has a circular dependency, which has multiple calls to separate threads. This again will cause issues.
I'm still not sure on the differences between Swift and Kotlin. In Swift when passing self into a function to be stored, the class storing it would mark the property as weak, like so:
weak var hostInterface: ......
Which will avoid any circular dependency from forming. A quick google says this isn't how things work in Kotlin. It might be better to look into the swift side passing in a closure (lambda on kotlin) and the kotlin side executing that. This might avoid the need to store a strong reference. Otherwise you need to be looking into some part of your code setting hostInterface back to null. Again its a bit hard to say only seeing some of the code and not knowing how its working.
In short, it looks like the code is very over complicated, and needs to be simplified, so that all these moving pieces can be tracked easier.
I have an array stored in a class that downloads its objects from the internet. My class is set up like so:
class StockManager {
var managerStock: [Dictionary<String, String>] {
return downloadStockFromDatabase()
}
...
}
I access the managerStock from other Swift files in my project like so, but it always resorts to re-downloading the stock again no matter if I have used the variable before (ie. recalls the function downloadStockFromDatabase):
let stockManager = StockManager()
print(stockManager.managerStock)
How would I make sure the managerStock only downloads once and I could use it in any of my files?
This is a question of correct software pattern usage. I would suggest:
make StockManager a singleton, so you will always access the same instance of it
initialize it e.g. in the AppDelegate, i.e. make sure it stays alive for the whole runtime
tip: call managerStock lazily, i.e. only when you really need it and not as part of initialization
As ff10 and holex suggested, make your class a singleton. It will look like this:
class StockManager {
static let sharedInstance = StockManager ()
var managerStock: [Dictionary<String, String>] {
return downloadStockFromDatabase()
}
...
}
Then access it using the static sharedInstance property:
print(StockManager.sharedInstance.managerStock)
I'm having trouble grasping the proper way of instantiating variables that always need to be set before an object is fully functional but may need to be instantiated after the constructor. Based on Swift's other conventions and restrictions it seems like there is a design pattern I'm unaware of.
Here is my use case:
I have a class that inherits from UIViewController and will programmatically create views based on user actions
I need to attach these views to this class, but to do so I need to retrieve their content based on configuration data supplied by another controller
I don't care if this configuration data is passed to the constructor (in which case it would always be required) or supplied by a secondary call to this object before it is used
My problem seems to be that both of the approaches in bullet 3 seem flawed.
In the first case, there is only one legitimate constructor this class can be called with, yet I'm forced to override other constructors and initialize member variables with fake values even if the other constructors are never intended to be used (I'm also trying to keep these variables as let types based on Swift's best practices).
In the second case, I'm effectively splitting my constructor into two parts and introduce an additional point of failure in case the second part fails to be called prior to class being used. I also can't move this second part to a method that's guaranteed to be called prior to usage (such as viewDidLoad) because I still need to pass in additional arguments from the config. While I can make sure to call the initPartTwo manually, I'd prefer to have a mechanism that better groups it with the actual constructor. I can't be the first one to run into this and it seems like there is a pattern I'm not seeing to make this cleaner.
UPDATE:
I ended up going with a modified version of the pattern matt suggested:
struct Thing {
let item1: String
let item2: String
struct Config {
let item3: String
let item4: String
}
var config:Config! {
willSet {
if self.config != nil {
fatalError("tried to initialize config twice")
}
}
}
init() {
self.item1 = ...
self.item2 = ...
...
}
public func phaseTwoInit(item3: String, item4: String) {
self.item3 = item3
self.item4 = item4
...
}
}
var t = Thing()
...
t.phaseTwoInit(...)
...
// start using t
If an initial instance variable property value can't be supplied at object initialization time, the usual thing is to declare it as an Optional. That way it doesn't need to be initialized by the class's initializers (it has a value - it is nil automatically), plus your code subsequently can distinguished uninitialized (nil) from initialized (not nil).
If the Optional if an implicitly unwrapped Optional, this arrangement need have no particular effect on your code (i.e. it won't have to be peppered with unwrappings).
If your objection is that you are forced to open the door to multiple settings of this instance variable because now it must be declared with var, then close the door with a setter observer:
struct Thing {
var name:String! {
willSet {
if self.name != nil {
fatalError("tried to set name twice")
}
}
}
}
var t = Thing()
t.name = "Matt" // no problem
t.name = "Rumplestiltskin" // crash
I have declared a Swift protocol:
protocol Option {
var name: String { get }
}
I declare multiple implementations of this protocol—some classes, some enums.
I have a view controller with a property declared as so:
var options: [Option] = []
When I try and set this property to an array of objects that implement the Option protocol in another VC's prepareForSegue, I get a runtime error:
fatal error: array cannot be bridged from Objective-C
Why doesn't this work? The compiler has all the information it needs, and I don't understand what Objective-C has to do with it at all—my project contains only Swift files, and these arrays aren't coming in or out of any framework methods that would necessitate them being bridged to NSArray.
I have found a solution. It is quite... unsatisfying, but it works. Where I set the array on the destination view controller I do:
destinationViewController.options = options.map({$0 as Option})
the compiler knows I'm passing in an Array of things that implement Option
You've let slip there a very revealing remark, which suggests the source of the issue. An "Array of things that implement Option" is not an Array of Option.
The problem is with the type of options back at the point where you create it (in prepareForSegue). You don't show that code, but I am betting that you fail to cast / type it at that point. That's why the assignment fails. options may be an array of things that do in fact happen to adopt Option, but that's not enough; it must be typed as an array of Option.
So, back in prepareForSegue, form your options like this:
let options : [Option] = // ... whatever ...
Now you will be able to assign it directly to destinationViewController.options.
Here's a quick test case (in a playground; I detest playgrounds, but they can have their uses):
protocol Option {
var name : String {get}
}
class ViewController : UIViewController {
var options : [Option] = []
}
enum Thing : Option {
var name : String {
get {
return "hi"
}
}
case Thing
}
let vc = ViewController()
let options : [Option] = [Thing.Thing]
vc.options = options // no problem
(I also tested this in an actual app with an actual prepareForSegue, and it works fine.)
I was having the same problem and fixed it marking my protocol with #objc, in your case it would look like this
#objc protocol Option {
var name: String { get }
}
Got the solution from this answer
This one also works fine
destinationViewController.options = options.map{$0}
I Wrote a function that return two Strings, when calling the function regularly its works fine, but when I'm running the function through loop, I'm getting this error:
Thread 1: EXC_BAD_ACCESS (code=2, address=0xbfffcba0)
override func viewDidLoad()
{
super.viewDidLoad()
test()
}
func test()
{
var funcs = [checkButton]
var a = checkButton(value: 1) // Runs OK
for f in funcs{
var result = f(value: 1) // Fail
}
}
func checkButton(#value: Int) -> (location: String, pattern: String){
return ("abc","cba")
}
Update:
I'm using Xcode 6 beta 2, and running Mavericks on VMware Workstation.
Also, I've just created new clean project with that code and still getting the error.
This code runs fine for me. Your EXC_BAD_ACCESS must be coming from some other part of your code. Try setting a breakpoint and stepping through the code to find the line throwing the error.
From the “The Swift Programming Language.”
“An instance method can be called only on a specific instance of the type it belongs to. It cannot be called in isolation without an existing instance.”
checkButton() is an instance method, not a closure. It works in the first case because there is an implicit self. before checkButton(). It will not work in the second case.
If you want to make checkButton a closure you could declare it like so:
let checkButton = { (#value: Int) -> (location: String, pattern: String) in
return ("abc","cba")
}
I can confirm that it doesn't work for me either. Created a iOS single-view app from template, and added above code. crash. As an experiment, I took it out of the array (just f = self.checkButton) and got the same result.
I think it's a bug in the compiler.
First according to the book, a method is actually a function which is actually a closure, albeit one with special properties and restrictions. Shouldn't self.checkButton (or implicit version) be sufficient to "give it an existing instance", making it a closure? If MattL is correct that instance methods can't be used as closures, then the compiler shouldn't allow you to assign one to anything.
Second, the crash occurs on the exit, not on the call. And if you reference self in checkButton, (e.g. println(self.title) having previously set title), it works fine. That suggests that the instance is indeed known and operating, just something wrong on the return.
Third, changing it to a class method doesn't help. Changing these lines
var a = ViewController.checkButton(value: 1)
var funcs = [ViewController.checkButton]
class func checkButton(#value: Int) -> (location: String, pattern: String)
results in the same crash. I don't see any similar prohibition on context for class methods.
Fourth, if you simply change the return type from (location: String, pattern: String) to just String and return abc, then the whole thing works fine.
Fourth, if you wrap test and checkButton in a new class testClass, and then call it as below, it works:
class testClass {
func test()
{
var funcs = [checkButton]
var a = checkButton(value: 1) // Runs OK
for f in funcs {
var result = f(value: 1) // Fail
println(result)
}
}
func checkButton(#value: Int) -> (location: String, pattern: String){
return ("abc","cba")
}
}
class ViewController: UIViewController {
override func viewDidLoad()
{
super.viewDidLoad()
let g = testClass()
g.test()
}
}
Now change testClass to testClass: NSObject and it crashes.
So it looks like the compiler is botching up a method return when called as a closure with a tuple in a Obj-C subclass. When I put it like that, I must say that it's not terribly surprising nobody's noticed yet; you're really pushing the edge here!
More practically, in the meantime, if it's helpful, an alternative to changing your method to a closure is to keep it unchanged and just wrap it as you put it in the array:
var funcs = [{value in self.checkButton(value: value)}]
This seems to work.