Swift Learnings

Back to square one

By ahchao in Learning Swift

September 11, 2021

I’ve been trying to learn some Swift lately (didn’t really learn/use it like properly properly) and I’m trying to document down some of my takeways or learning points. This is going to be a on-going and confusing document, but from time to time I will try to reorganise this.

struct and enum is similar - they are both value type.

enum carry data too! but no stored properties; functions/computed props are fine.

class ?are reference type.

struct are stored on the stack, classes are stored on the heap.

However, Swift has this thing called stack promotion, where classes can be stored on the stack in the situation allows. So the above point is not always strictly followed. Smart swift!

structs are preferred in swift (https://developer.apple.com/documentation/swift/choosing_between_structures_and_classes), while classes is only when required (e.g. need to be shared by many people or identity related).

structs are ‘faster’ compared to classes since the cost of copying, alloc+dealloc, reference counting, is higher on classes. Since it is also copy-on-write, (AFAIK) arrays and dictionaries are also copied but on reference level (i.e. still pointing to same data), but once you modify it, it then copies for real. Think of it as lazy/delayed copy, so actual duplication of array/dictionary data only takes effect when it actually happens. We don’t get this COW ‘feature’ for free with our own data types. Source: https://www.hackingwithswift.com/example-code/language/what-is-copy-on-write

Also in some situation, even struct objects can be allocated to the heap or costs even more than a class, e.g. a struct (value type) having vars of class (reference type) and we have copies of that struct (causing more retain count on those vars of class).

When a struct conforms to a protocol it also has higher allocation costs.

There is no need for ‘new’, everything is as per above. i know this is counter-intuitive to what I learnt in cpp where new = heap and non-new = stack. Swift does everything for you based on value/reference type.

Swift has functional aspects and types like in javascript/typescript. The confusing part is that swift is var (variable) and let for consts, but in js/ts it’s let and const respectively.

Source: https://www.vadimbulavin.com/value-types-and-reference-types-in-swift/

Also some recap:

  • Objc/Swift uses ARC, automatic reference counting, which is an ownership system. Basically concept is like smart pointer in C++. Whenever something new reference the referenced object, the retain count of that referenced object is +1, and if they are set to nil, retain count is -1. Same in Objc, it has weak and strong concept. If retain count is 0, that object is doomed to die. Because of the ownership system the one thing we need to take note of is 2 objects having a strong reference to each other, causing the system to not know when to delete them since retain count is forever +1… When retain count is 0, any weak references are zeroed out (i.e. set to nil). In Swift, they also introduced unowned, basically it’s a flavour of weak, which I think it’s mainly used if you are sure the object you are referencing will not be zeroed out or app will crash (so it is more tightly coupled?). We should prefer to use weak when it is not clear if the captured object will outlive our var/closure. - Apparently for weak references, this thing called Side-Tables entry is how the retain count are being tracked and weak reference points to them (and not the actual object in the heap).
  • It is also cool to know that the destruction of the reference object is not destroyed immediately - they have a life cycle too (live -> deiniting -> deinited -> freed -> dead), and depending on the weak/unowned/strong references that is left remaining that reference to it, the life cycle stage in which it is removed from memory differs. Source: https://www.vadimbulavin.com/swift-memory-management-arc-strong-weak-and-unowned/

There are so many things happening in Swift that’s quite different from Objective-C and JavaScript/TypeScript, and in order to use it well, we have to understand the basic memmory first. I once had NSTimers running in Objective-C because I didn’t know it has reference to the view controller, so both didn’t get destroyed and I was bewildered for a while. So this is good to know (and very basic!). Also excited to be using functional programming on Swift!

More good resources:

Access level in swift. everything is internal by default unless otherwise stated. Note private -> fileprivate -> internal (default) -> public/open. So if your class is public, to prevent accidents, implicility your members are all still internal. For unit testing, we can access up to internal if we mark it as @testable (not clear yet on how unit testing work). If we have a tuple of mixed access type, the most restrictive one will be set. And no, we cannot set access level for tuple.

Naming!

Functions and methods are sometimes used interchangeably as both usable code and are defined by func keyword, but in Swift, methods are functions that belong to a class/struct/enum only, and it has self. Of course we want functions like max or zip (which is like known/standard) but we wouldn’t want all our functions to be in the ‘public’ or ‘free’, since it’s like global(?) and polluting namespace(?).

Properties

Properties itself by definition are associated values for class/struct/enum.

Naming of properties

Swift has many kinds of properties it’s so confusing.. Seriously, please correct me if I am wrong.

First, variable vs property. This is basically same thing as the above functions/methods. They are all variables, but property is associated with class/struct/enum. Moving on.

Type Property - class/enum/struct-level properties (read: static that we are used to). Instance Property - the usual instance we are used to when we “new” an object in the heap/stack.

Now, for each of the above, they can be further categorised:

  • Stored Property - the usual variable we are used to. Note that enum cannot have stored property.

    • This can also be further categorised: Lazy Stored Property (var available only) - delay initialisation of the variable but multithreading can screw this up and init multiple times so need to beware!!!
    • You thought that’s all? NO!! Stored Property is upgraded to be easier than Objective-C, now we can observe and react based on willSet and didSet function. This is called Property Observer; they monitor changes and we can respond accordingly. You can access the newValue in willSet and oldValue in didSet in case you need it. More confusingly, if we are using this in a class situation, we can do it to our superclass and subclass too.
  • Computed Property - basically think of it as a get function as they dont store property. counter-intuitively they also have a optional set function feature. Useful for e.g. computing a box’s center point given it’s origin and size on runtime. Like I said, getter/setter, but not good to go overboard with this, else just use it to call a function if it’s heavy.

    • Hold up, the willSet and didSet in Stored Property applies to us too, but only for subclass classes’s Computer Property… IKR…
    • HOLD UP! Swift also introduced get and set here too, and you cannot reference itself inside the get and set else there will be stack overflow as it kept going into a loop forever. So they have some named parameter like newValue for set to use, kind of like in Objective-C we used _ivarXXX as the intermediate variable.

    Property Observer - similar to Computer Property, TBC…

Needless to say, static properties cannot access any instance variables/functions.

More random(?) info:

  • If you let a struct, you cannot change it’s properties even if they are vars, but you can do it for class object. Since struct is value type, when we mark the struct instance itself as read-only, everything inside is read-only. Guess that make sense.
  • mutating keyword is for methods inside struct only. Cos we want struct to be like immutable most of the time if possible but that’s not always possible. so we should treat mutating as a “red flag/warning” that could be potential for errors (which is the point of struct, to prevent issues).
  • In Swift, Array, String, and Dictionary are all value types. Set, I don’t know.
  • Swift init is clearer and easier than Objective-C’s. No value return or no if (self) nonsense. Purely to init vars. For struct it is autogenerated unless you override it, for class and enum too they have it. Property Observers are not called when we set directly the default value of the Stored Property or in our inits.
  • In Objective-C/C/C++, enums are 0 by default and ints only. In Swift, you set it to what you want, Int, String, and ‘autoconverting’ them by using the protocol like String to direct convert to string when you .rawValue.

Protocols

It is similar to Objective-C (I think), but in ObjC it is mainly used for delegates kind of usage. So protocols are new things in Swift, and many say Swift is more like a protocol-oriented programming, and that seems to be the case from what I seen so far.

All class/struct/enum can adopt protocols. Used it to separate (forgot what i wanted to say)

Protocols can inherit protocols too. class/struct/enum can implement multiple protocols.

We can use it as a type itself but it’s very rare. We also can’t use it for View, Equatable, Identifiable.

For generics, we can use it like struct MemoryGame<CardContent> where CardContent: Equatable.

We can also use Protocol to restrict extensions, so that the extensions only apply for certain scenarios.

Goold old C++ operator overloading has made a comeback in Swift.

https://github.com/FiveStarsBlog/WWDCNotes is really good website for reading a summary of what’s new in WWDC.

Last updated: 19th Sept 2021. Will keep adding as I learn.

comments powered by Disqus