0

My main code is to long to put here so i'll try with a simpler explanation. Let's say i have a global variable called "count" that gets used by functions. If count was to get called twice for w.e reason as shown in the code below... how can i make it so the output "10 , 10" rather then "10 , 20".

var count = 0

override func viewDidLoad() {

  helloWorld()

}


override func viewWillAppear(_ animated: Bool) {
    helloWorld()
}



func helloWorld(){
    var counter = 0
    for i in 1...10{
        count = count + 1
        counter = counter + 1
    }
    print(count)
}
rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • 1
    Possible duplicate of [Whither dispatch\_once in Swift 3?](https://stackoverflow.com/questions/37801407/whither-dispatch-once-in-swift-3) – ielyamani Sep 27 '18 at 20:56

2 Answers2

2

Use DispatchQueue with static token (cached through app lifecycle), here's a useful extension:

extension DispatchQueue {

    private static var tokens: [String] = [] // static, so tokens are cached

    class func once(executionToken: String, _ closure: () -> Void) {
        // objc_sync_enter/objc_sync_exit is a locking mechanism
        objc_sync_enter(self)
        defer { objc_sync_exit(self) }
        // if we previously stored token then do nothing
        if tokens.contains(executionToken) { return }
        tokens.append(executionToken)
        closure()
    }
}

func doSomething() {
    DispatchQueue.once(executionToken: "doSomething") {
        print("This should be executed once")
    }
}

doSomething()
doSomething()
doSomething()

"This should be executed once" will be printed only once.

You can also use the lazy mechanism:

class SomeClass {
    lazy var executeOnce: () -> Void = {
        doSomething()
        return {}
    }()

    func doSomething() {
        print("This should be executed once")
    }
}

let someClass = SomeClass()
someClass.executeOnce()
someClass.executeOnce()
someClass.executeOnce()

It's thread-safe out of the box so you don't need to worry about locking.

Oleh Zayats
  • 2,423
  • 1
  • 16
  • 26
  • I'm trying to implement the lazy option in my main code. It works when the application is initialized, but when i leave the view controller and come back to the page. My executeOnce function doesn't even get called. Could it be because my view controller is being instantiated also with a "Lazy". – iloveStackOverflow Sep 27 '18 at 22:13
0

Change

func helloWorld(){
var counter = 0
for i in 1...10{
    count = count + 1
    counter = counter + 1
}
print(count)
}

to

func helloWorld(){
var counter = self.count
for i in 1...10{
    count = count + 1
}
print(count)
self.count = counter
}

This way you store into counter the count value before it changes, then make it equal to the previous one again.

NOTE: This way you will always get 10 when you call helloWorld(), no matter how much time you call the function.

You can change the function as shown below:

func helloWorld(_ countValue: Int){
var counter = self.count
for i in 1..<countValue{
    count = count + 1
}
print(count)
self.count = counter

}

So you can choose the upper element of the counter simply by calling:

helloWorld(10)
helloWorld(20)

ADVICE: it's not a good practice to have a function that changes an external value

Andrew21111
  • 868
  • 8
  • 17