Classes & Reference Types
Classes bring reference semantics, inheritance, and lifecycle hooks. By the end of this lesson you'll define class es, write init / deinit , use inheritance with override , and clearly see how a class differs from a struct.
Learn Classes & Reference Types in our free Swift course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…
Part of the free Swift course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
- • Define classes with class and write your own init
- • Understand reference semantics: variables share one instance
- • Use the lifecycle hooks init and deinit
- • Build hierarchies with inheritance and override
- • Decide between struct and class with confidence
1️⃣ Defining a Class
Declare a class with its properties and methods. Unlike structs, classes have no free memberwise initializer — you write your own init to set up stored properties.
2️⃣ Reference Semantics (Shared Instances)
This is the heart of classes: assigning one to a new variable makes both refer to the same object . A change through one is seen through the other — the opposite of a struct's independent copy.
3️⃣ Lifecycle: init & deinit
init runs when you create an instance; deinit runs automatically just before the instance is freed — perfect for cleanup like closing files or removing observers.
4️⃣ Inheritance & override
Only classes can inherit. A subclass extends a superclass and replaces inherited methods with override , enabling polymorphism — treating different subclasses through a shared base type.
Your turn. Fill in the blanks, then run it and check the output.
Pro Tips
- 💡 Default to structs. Use a class only when you need shared mutable state, identity, or inheritance.
- 💡 Watch shared mutation. Because references share one object, changes ripple to every holder — powerful, but a common source of bugs.
- 💡 Call super.init from a subclass initializer to set up the inherited part.
- 💡 Use === to test whether two references point at the very same instance (identity).
Common Errors (and the fix)
- "Class 'Dog' has no initializers" — classes don't auto-generate one. Write an init that sets every stored property.
- "Property 'self.x' not initialized at super.init call" — set your own properties before calling super.init .
- "Overriding declaration requires an 'override' keyword" — add override when replacing an inherited method.
- Unexpected shared changes — two variables pointing at one instance both see edits. If you wanted independence, use a struct.
📋 Quick Reference
Feature
struct
class
Semantics
value (copy)
reference (shared)
Memberwise init
yes (free)
no (write your own)
Inheritance
no
yes
deinit
Mutating keyword
required
not needed
Identity (===)
n/a
Frequently Asked Questions
Mini-Challenge: Vehicles with Inheritance
Build it, run it, and check your output against the example in the comments.
🎉 Lesson Complete!
- ✅ class defines a reference type — variables share one instance
- ✅ Write your own init ; use deinit for cleanup
- ✅ Classes support inheritance with override
- ✅ struct = value/copy; class = reference/shared — choose deliberately
- ✅ Next lesson: Properties: Stored, Computed & Observers
Practice quiz
What keyword defines a class?
- struct
- object
- class
- type
Answer: class. Classes are declared with the class keyword.
Are classes value types or reference types?
- Reference types
- Value types
- Neither
- Both
Answer: Reference types. Classes are reference types — variables share the same instance.
Given let a = Box(); let b = a; b.value = 9 — what is a.value?
- unchanged old value
- nil
- compile error
- 9 (a and b share the same instance)
Answer: 9 (a and b share the same instance). Both a and b reference the same object, so a.value is also 9.
Do classes get a free memberwise initializer like structs?
- Yes
- No — you must write your own init
- Only with @auto
- Only for one property
Answer: No — you must write your own init. Classes require you to write an initializer for stored properties without defaults.
What is a deinitializer (deinit) used for?
- Cleanup right before an instance is freed
- Creating instances
- Copying instances
- Comparing instances
Answer: Cleanup right before an instance is freed. deinit runs just before a class instance is deallocated, for cleanup.
Which feature do classes support that structs do not?
- Methods
- Computed properties
- Inheritance from another class
- Protocols
Answer: Inheritance from another class. Only classes support inheritance from a superclass.
What keyword marks a method as overriding a superclass method?
- super
- override
- virtual
- replace
Answer: override. Subclasses use the override keyword to replace inherited methods.
Do you need 'mutating' to change a class property inside a method?
- Yes, always
- Only for let properties
- Only in subclasses
- No — classes are reference types, so it's not needed
Answer: No — classes are reference types, so it's not needed. mutating is a value-type concept; class methods can change properties freely.
Can you change a property of a class instance stored in a constant?
- No, never
- Yes — let fixes the reference, not the object's var properties
- Only the first property
- Only with override
Answer: Yes — let fixes the reference, not the object's var properties. A let class reference is fixed, but the instance's var properties can still change.
When should you choose a class over a struct?
- For all data
- Never
- When you need shared mutable state, identity, or inheritance
- Only for numbers
Answer: When you need shared mutable state, identity, or inheritance. Classes suit reference semantics: shared state, identity, and inheritance.
Continue this course
- Previous: Structs & Value Types
- Next: Properties: Stored, Computed & Observers