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
weakandstrongconcept. 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 astrongreference to each other, causing the system to not know when to delete them since retain count is forever +1… When retain count is 0, anyweakreferences are zeroed out (i.e. set tonil). In Swift, they also introducedunowned, basically it’s a flavour ofweak, 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 useweakwhen it is not clear if the captured object will outlive our var/closure. - Apparently for weak references, this thing calledSide-Tablesentry is how the retain count are being tracked andweakreference 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/strongreferences 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:
- https://www.swiftbysundell.com/articles/property-wrappers-in-swift/
- https://www.vadimbulavin.com/value-types-and-reference-types-in-swift/
- https://www.vadimbulavin.com/unit-testing-best-practices-on-ios-with-swift/
- https://github.com/raywenderlich/swift-style-guide#lazy-initialization
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
enumcannot have stored property.- This can also be further categorised: Lazy Stored Property (
varavailable 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
willSetanddidSetfunction. This is called Property Observer; they monitor changes and we can respond accordingly. You can access thenewValueinwillSetandoldValueindidSetin 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.
- This can also be further categorised: Lazy Stored Property (
-
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
willSetanddidSetin Stored Property applies to us too, but only for subclass classes’s Computer Property… IKR… - HOLD UP! Swift also introduced
getandsethere too, and you cannot reference itself inside thegetandsetelse there will be stack overflow as it kept going into a loop forever. So they have some named parameter likenewValueforsetto use, kind of like in Objective-C we used_ivarXXXas the intermediate variable.
Property Observer - similar to Computer Property, TBC…
- Hold up, the
Needless to say, static properties cannot access any instance variables/functions.
More random(?) info:
- If you
leta struct, you cannot change it’s properties even if they arevars, but you can do it forclassobject. Sincestructis value type, when we mark thestructinstance itself as read-only, everything inside is read-only. Guess that make sense. mutatingkeyword is for methods insidestructonly. Cos we wantstructto 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 ofstruct, to prevent issues).- In Swift,
Array,String, andDictionaryare all value types.Set, I don’t know. - Swift
initis clearer and easier than Objective-C’s. No value return or noif (self)nonsense. Purely to init vars. Forstructit is autogenerated unless you override it, forclassandenumtoo they have it. Property Observers are not called when we set directly the default value of the Stored Property or in ourinits. - 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 likeStringto 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.