Swift: Classes e Structs do Swift

Classes e structs são os dois tipos principais para construir modelos de dados no Swift. Eles compartilham muitas semelhanças, mas diferem fundamentalmente no gerenciamento de memória e em como os valores são passados. Esta lição ajuda a esclarecer a estratégia de seleção para cada um.

1. O Que Você Vai Aprender


2. Uma História Real de uma Desenvolvedora iOS

(1) Problema: Alterar uma Conta de Usuário em um Lugar Afeta Outro

Alice escolheu classe para modelar uma conta de usuário:

SWIFT
class UserAccount {
    var name: String
    var balance: Double
    init(name: String, balance: Double) {
        self.name = name
        self.balance = balance
    }
}
let account1 = UserAccount(name: "Alice", balance: 1000)
let account2 = account1  // References the same instance
account2.name = "Bob"
print(account1.name)  // "Bob" — account1 was also changed!

Alice só queria criar um novo usuário, mas acidentalmente alterou os dados originais. Os efeitos colaterais dos tipos por referência se propagaram por toda a base de código sem que ela percebesse.

(2) Solução: Struct como Tipo por Valor

SWIFT
struct UserAccount {
    var name: String
    var balance: Double
}
var account1 = UserAccount(name: "Alice", balance: 1000)
var account2 = account1  // Copies an independent set of data
account2.name = "Bob"
print(account1.name)  // "Alice" — account1 is unaffected

A atribuição de struct realiza uma cópia de valor — cada instância é independente.

(3) Benefício: Comportamento de Dados Previsível

Dimensão classe (Tipo por Referência) struct (Tipo por Valor)
Comportamento de atribuição Compartilha a mesma instância Cópia independente
Risco de mutação acidental Alto (aliasing) Nenhum
Alocação de memória Alocação no heap Alocação na pilha (mais eficiente)
Segurança de thread Requer sincronização manual Naturalmente isolada
Adequado para Estado mutável compartilhado Modelos de dados, semântica de valor

3. Definindo Classes e Structs

(1) Comparação de Sintaxe Básica

100%
graph TB
    subgraph classe
        A["class Pessoa {"]
        B["    var nome: String"]
        C["    var idade: Int"]
        D["    func saudar() { }"]
        E["}"]
    end
    subgraph struct
        F["struct Pessoa {"]
        G["    var nome: String"]
        H["    var idade: Int"]
        I["    func saudar() { }"]
        J["}"]
    end
Característica classe struct Observações
Propriedades Sim Sim Propriedades armazenadas e computadas
Métodos Sim Sim Métodos de instância e de tipo
Inicializadores Sim Sim classe não gera automaticamente init por membro
Herança Sim Não struct não pode herdar
Desinicializador Sim Não struct não tem deinit
Contagem de referência Sim Não struct não precisa de ARC
Conversão de tipo Sim Não as?/as! apenas para classe

▶ Exemplo: Definindo Classes e Structs

SWIFT
// ============================================
// Class and struct definition comparison
// ============================================
// Class definition — must write init manually
class PersonClass {
    var name: String
    var age: Int
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    func introduce() {
        print("Hi, I'm \(name), \(age) years old")
    }
}
// Struct definition — automatically gets a memberwise initializer
struct PersonStruct {
    var name: String
    var age: Int
    func introduce() {
        print("Hi, I'm \(name), \(age) years old")
    }
}
let p1 = PersonClass(name: "Alice", age: 28)
p1.introduce()
let p2 = PersonStruct(name: "Bob", age: 22)  // Auto-generated init
p2.introduce()

Saída:

TEXT 📖 Somente leitura
Hi, I'm Alice, 28 years old
Hi, I'm Bob, 22 years old

4. Tipos por Valor vs Tipos por Referência

(1) Diferenças Principais

100%
graph LR
    subgraph "Tipo por Valor (struct)"
        A["var a = Ponto(x: 1, y: 2)"]
        B["var b = a"]
        C["b.x = 5"]
        D["a.x → 1 (independente)"]
        A --> B --> C --> D
    end
    subgraph "Tipo por Referência (classe)"
        E["var a = Ponto(x: 1, y: 2)"]
        F["var b = a"]
        G["b.x = 5"]
        H["a.x → 5 (compartilhado)"]
        E --> F --> G --> H
    end
Dimensão Tipo por Valor (struct) Tipo por Referência (classe)
Semântica de atribuição Copia conteúdo Copia referência
Verificação de igualdade == compara valores === compara identidade
Local de armazenamento Pilha (mais rápido) Heap (gerenciado por ARC)
Constante let Propriedades são imutáveis Referência é imutável mas propriedades podem mudar
Parâmetros de função Cópia por padrão Compartilha referência por padrão

▶ Exemplo: Comparação de Comportamento de Atribuição

SWIFT
// ============================================
// Value type vs reference type assignment behavior
// ============================================
// Struct — value type
struct Position {
    var x: Int
    var y: Int
}
var posA = Position(x: 10, y: 20)
var posB = posA
posB.x = 99
print("posA: (\(posA.x), \(posA.y))")  // Unchanged
print("posB: (\(posB.x), \(posB.y))")  // Only affects posB
// Class — reference type
class Location {
    var x: Int
    var y: Int
    init(x: Int, y: Int) { self.x = x; self.y = y }
}
var locA = Location(x: 10, y: 20)
var locB = locA
locB.x = 99
print("locA: (\(locA.x), \(locA.y))")  // Also changed!
print("locB: (\(locB.x), \(locB.y))")

Saída:

TEXT 📖 Somente leitura
posA: (10, 20)
posB: (99, 20)
locA: (99, 20)
locB: (99, 20)

(2) O Operador de Identidade ===

Tipos por referência podem usar === para verificar se duas variáveis apontam para a mesma instância:

SWIFT
let loc1 = Location(x: 5, y: 10)
let loc2 = loc1
let loc3 = Location(x: 5, y: 10)  // Different instance
print(loc1 === loc2)  // true — same instance
print(loc1 === loc3)  // false — different instances

Dica: === é o operador de identidade, distinto de == (o operador de igualdade). Tipos por valor só podem usar == (devem implementar Equatable) e não podem usar ===.


5. Métodos mutating e Estratégia de Seleção

(1) A Palavra-chave mutating

Os métodos de instância de struct não podem modificar propriedades por padrão. Para fazê-lo, eles devem ser marcados como mutating:

Tipo de Método classe struct (não-mutating) struct (mutating)
Modificar propriedade Permitido Erro de compilação Permitido
Modificar self Permitido Não permitido Permitido
Requisito de chamada Sem restrição Pode ser chamado em constante let Deve ser chamado em var

▶ Exemplo: Métodos mutating

SWIFT
// ============================================
// Struct mutating methods
// ============================================
struct Counter {
    var count = 0
    // Non-mutating — read-only method
    func display() {
        print("Count: \(count)")
    }
    // mutating — modifies property
    mutating func increment() {
        count += 1
    }
    // mutating — replaces the entire instance
    mutating func reset() {
        self = Counter()  // Replace self
    }
}
var counter = Counter()  // Must be var to call mutating methods
counter.increment()
counter.increment()
counter.display()   // Count: 2
counter.reset()
counter.display()   // Count: 0

Saída:

TEXT 📖 Somente leitura
Count: 2
Count: 0

(2) Estratégia de Seleção

Cenário Recomendado Motivo
Modelos de dados (Ponto, Tamanho, Usuario) struct Semântica de valor, seguro e previsível
Precisa de herança (subclasse UIView, BaseViewController) classe Herança é exclusiva de classe
Precisa de estado mutável compartilhado classe Tipos por referência compartilham automaticamente
Precisa de interoperabilidade com ObjC classe @objc requer classe
Wrapper leve (envolvendo poucos valores) struct Alocação na pilha, zero sobrecarga
Precisa de limpeza com deinit classe struct não tem deinit

▶ Exemplo: Struct como Escolha Padrão para Modelos

SWIFT
// ============================================
// Why modern Swift prefers struct for models
// ============================================
struct User {
    let id: Int
    var name: String
    var email: String
    var score: Int
    mutating func addScore(_ points: Int) {
        score += points
    }
}
var user = User(id: 1, name: "Alice", email: "alice@example.com", score: 100)
user.addScore(50)
// Copy-and-mutate pattern — safely create changed versions
let admin = User(id: 2, name: "Admin", email: "admin@example.com", score: 0)
// admin.addScore(10)  // Compile error! let constant cannot call mutating methods
var editor = admin
editor.name = "Editor"  // Independent modification, doesn't affect admin

Saída:

TEXT 📖 Somente leitura
(No console output — demonstrates value semantics: editor.name = "Editor" does not affect admin.name)

6. Exemplo Completo: Sistema de Conta Bancária

SWIFT
// ============================================
// Complete example: Bank account system
// Features: class for account management, struct for transaction records
// ============================================
import Foundation
// 1. Transaction record — value type struct
struct Transaction {
    let id: Int
    let amount: Double
    let type: String  // "deposit" or "withdraw"
    let date: Date
    func description() -> String {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd"
        return "[\(formatter.string(from: date))] \(type): $\(amount)"
    }
}
// 2. Account — reference type class (shared state)
class BankAccount {
    let accountNumber: String
    var balance: Double
    private var transactions: [Transaction] = []
    private var nextId = 1
    init(accountNumber: String, initialBalance: Double = 0) {
        self.accountNumber = accountNumber
        self.balance = initialBalance
    }
    func deposit(amount: Double) {
        guard amount > 0 else { return }
        balance += amount
        let tx = Transaction(id: nextId, amount: amount, type: "deposit", date: Date())
        transactions.append(tx)
        nextId += 1
    }
    func withdraw(amount: Double) -> Bool {
        guard amount > 0 && amount <= balance else { return false }
        balance -= amount
        let tx = Transaction(id: nextId, amount: amount, type: "withdraw", date: Date())
        transactions.append(tx)
        nextId += 1
        return true
    }
    func printStatement() {
        print("=== Account \(accountNumber) ===")
        print("Balance: $\(balance)")
        print("Transactions:")
        for tx in transactions {
            print("  \(tx.description())")
        }
    }
}
// 3. Usage
let account = BankAccount(accountNumber: "ACC-001", initialBalance: 1000)
account.deposit(amount: 500)
account.withdraw(amount: 200)
account.deposit(amount: 100)
account.printStatement()

Saída:

TEXT 📖 Somente leitura
=== Account ACC-001 ===
Balance: $1400.0
Transactions:
  [2026-07-30] deposit: $500.0
  [2026-07-30] withdraw: $200.0
  [2026-07-30] deposit: $100.0

❓ Perguntas Frequentes

P: Por que a Apple recomenda preferir struct? R: Tipos por valor são mais seguros e previsíveis. Sem efeitos colaterais de referências compartilhadas, sem ciclos de retenção e sem sobrecarga de contagem de referência. Tipos da biblioteca padrão do Swift como String, Array e Dictionary são todos structs. P: struct tem vantagem de desempenho? R: Sim. Structs são alocadas na pilha, sem sobrecarga de contagem de referência e sem custo de alocação no heap. No entanto, passar structs grandes entre funções pode incorrer em custos de cópia mais altos comparado a passar uma referência. O mecanismo Copy-on-Write do Swift otimiza isso. P: Devo sempre escrever init para uma classe manualmente? R: Para uma classe, se todas as propriedades tiverem valores padrão e nenhum inicializador for definido, o Swift gera um init padrão sem argumentos. Mas ele nunca gera automaticamente um init por membro como a struct faz. P: struct pode ter propriedades lazy? R: Sim. lazy var funciona em structs também, mas como propriedades lazy não podem ser acessadas em structs imutáveis, o uso prático é limitado. lazy é mais natural em classes. P: mutating pode ser usado em classes? R: Não. mutating só se aplica a tipos por valor (struct e enum). Métodos de classe podem modificar propriedades por padrão — sem necessidade da palavra-chave mutating.


📖 Resumo


📝 Exercícios

  1. Básico: Defina uma struct Livro com propriedades titulo, autor, paginas e um método resumo(). Crie duas instâncias e teste o comportamento de cópia de tipo por valor.
  2. Intermediário: Defina uma classe SensorTemperatura com uma propriedade tempAtual e um array de histórico leituras: [Double]. Implemente os métodos registrarTemp(_:) e media(). Crie uma instância, registre 5 temperaturas e imprima a média.
  3. Desafio: Projete um "Sistema de Colaboração em Equipe": use uma classe Equipe para gerenciar uma lista compartilhada de membros e uma struct Membro para perfis individuais. Equipe fornece os métodos adicionarMembro(_:) e listarMembros(). Membro tem um método mutating para atualizar funções. Verifique o comportamento de referência compartilhada da classe.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%