0

I have a ViewController where there's a logic for the "known" and "unknown" location. At a high level it looks like this:

class MyViewController {
    private var myVar: CLLocationCoordinate2D? // is updated somewhere

    private var myFunc1() {
        let someCond = myVar == nil // "isLocationUnknown" logic
    }

    private var myFunc2() {
        guard let myVar == nil else { return } // "isLocationUnknown" logic
    }
} 

Now there's a requirement to handle the "invalid location" case. Which means in addition to the nullability check, the CLLocationCoordinate2D check should be performed.

And I can't decide what is better ( as I don't know where to learn about Swift implementation details except of reading the sources :) ) :

Approach #1:

private func isLocationUnknown(_ location: CLLocationCoordinate2D?) -> Bool {
    guard let location = location else {
        return true
    }
    return !CLLocationCoordinate2DIsValid(location)
}

Approach #2:

private extension Optional where Wrapped == CLLocationCoordinate2D {
    var isUnknown: Bool {
        guard let self = self else {
            return true
        }
        return !CLLocationCoordinate2DIsValid(self)
    }
}

The criterias of comparison:

  1. semantics: I guess #2 is more "swifty"/expressive etc
  2. compilation time: can there be any difference (at scale)?
  3. run-time performance: can there be any difference (at scale)?

I believe in this particular case all criterias are not important, but if this enum would be public, and called e.g. many times per second, or within multiple places in the codebase, then I'd like to be more confident when making the decision.

olha
  • 2,132
  • 1
  • 18
  • 39
  • 1
    I'd probably prefer #2 due to its Swifty-ness, but that's just opinion. If you truly think there's a performance concern (it's hard for me to envision a scenario where that could be true), then you can always run a test and benchmark it. – jnpdx Jun 01 '22 at 00:43
  • 1
    Arguably, it's not the job of the view controller to know about this location in the first place. Checking if the GPS is working isn't "controlling a view". – Alexander Jun 01 '22 at 02:06

1 Answers1

2

A class and func are reference types and stored on the heap and is therefore slower to access. Since the type Optional is an enum it is stored on the stack and will be quicker to access. But in this case it would probably not be a noticeable difference. Do which ever you feel is right.

If you want to read about memory management, here's a good article: https://manasaprema04.medium.com/memory-management-in-swift-heap-stack-arc-6713ca8b70e1m

And here is a question about stack vs. heap memory: Swift stack and heap understanding

EDIT: Another thought is that your first approach takes CLLocationCoordinate2D as a parameter which creates a new copy of CLLocationCoordinate2D. So the space complexity is probably larger as well. But if that's the case, minuscule.

Olle Ekberg
  • 804
  • 8
  • 11
  • "A class and func is stored on the heap" that's not really true, and the performance difference here is moot in any case. "your first approach takes CLLocationCoordinate2D as a parameter which creates a new copy of CLLocationCoordinate2D." This is also not really true, and also moot. – Alexander Jun 01 '22 at 02:07
  • I agree that it won't effect perfomance. "..But in this case it would probably not be a noticeable difference". But a class is a reference type and stored on the heap. – Olle Ekberg Jun 01 '22 at 03:10
  • I presume you mean instances of classes (not classes themselves, which are stored statically). Objects are often allocated on the heap, but very often, when the optimizer's data-flow analysis can prove their lifetime doesn't escape a particular context, they can be stack-allocated, or even register allocated. – Alexander Jun 01 '22 at 03:12
  • Likewise with functions, functions that don't capture over any non-static state have no need to be dynamically allocated. They're stored in the program text not unlike regular ol' C functions. And again, even when they do capture state, if they're not `@escaping`, the optimizer can inline them. (IIRC even if they are `@escaping` inlining is still possible in some cases, such as when module optimization is enabled, or a calling library API marked `@usableFromInline`) – Alexander Jun 01 '22 at 03:15
  • Yes sorry, an instance. So would the optimizer allocate this instance of MyViewController on the heap or the stack? Surely the heap? – Olle Ekberg Jun 01 '22 at 03:17
  • View controllers are typically always escaping (if they didn't out-live the function that created them, how would they be used by the view hierarchy), then it would be allocated on the heap. But that's beside the point, none of the OP's options are about changing the VC to not be a class (in fact, this is impossible in UIKit/AppKit). – Alexander Jun 01 '22 at 13:14
  • 1
    Also, I'll not that the "objects are fast to access" generalization doesn't quite hold. It's true that accessing values directly is faster than accessing objects through one layer of pointer indirection, but in practice, you also have to weigh the cost of passing values around. Small values are usually fastest, because they avoid the need for ARC retains/releases that objects would need. However, large values (which need a lot of memory to be copied) and values that contain references (which need to be retained/released) can be slower than pobjects. – Alexander Jun 01 '22 at 13:16
  • That's a great point. – Olle Ekberg Jun 01 '22 at 13:28
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/245231/discussion-between-alexander-and-olle-ekberg). – Alexander Jun 01 '22 at 13:31
  • 1
    My more general point is, that these (often-inaccurate) generalizations aren't particularly useful as a predictive tool for guessing about performance. And in any case, this probably isn't a critical region, so it just doesn't matter. It's better to focus on correct abstractions, readability, etc. – Alexander Jun 01 '22 at 13:32