Introdução: a eterna disputa entre static e class em Swift
\nSe você já programou em Swift, certamente já se deparou com a seguinte situação: precisa criar um método ou variável que pertence ao próprio tipo, e não à sua instância. E surge o dilema: usar a palavra-chave static ou class? No Stack Overflow, essa pergunta acumulou milhares de visualizações, e não é à toa: a diferença é sutil, mas crucial para o design correto da arquitetura do aplicativo.
Neste tutorial, vamos entender de uma vez por todas as diferenças entre static e class em Swift, quando usar cada um e como não cair em armadilhas com herança. Vamos lá!
O que são propriedades e métodos de tipo?
\nAntes de mergulharmos nos detalhes, vamos relembrar o básico. Em Swift, cada classe, estrutura ou enumeração tem dois tipos de membros:
\n- \n
- De instância (instance) — pertencem a um objeto específico. Para usá-los, é preciso criar uma instância:
let obj = MyClass(). \n - De tipo (type) — pertencem ao próprio tipo. São chamados pelo nome do tipo:
MyClass.someMethod(). \n
Para criar propriedades e métodos de tipo em Swift, usamos as palavras-chave static e class. E é aqui que a coisa fica interessante.
Static: rígido e imutável
\nA palavra-chave static é sua amiga confiável quando você quer criar uma propriedade ou método que não pode ser substituído em uma subclasse. Ela funciona da mesma forma para classes, estruturas e enumerações.
Vamos ver um exemplo. Imagine que estamos criando um aplicativo para registrar estudantes e queremos armazenar o número total de objetos criados:
\n\nclass Student {\n static var totalCount = 0\n \n let name: String\n \n init(name: String) {\n self.name = name\n Student.totalCount += 1\n }\n \n static func printTotal() {\n print("Total de estudantes: \\(totalCount)")\n }\n}\n\nlet s1 = Student(name: "Ivan")\nlet s2 = Student(name: "Maria")\nStudent.printTotal() // Exibe: Total de estudantes: 2\n\nObserve que acessamos totalCount e printTotal() pelo nome da classe Student, e não por uma instância. Isso é útil para contadores, configurações padrão ou cache de dados.
Static em estruturas e enumerações
\nEm estruturas e enumerações, static é a única maneira de declarar membros de tipo. Por exemplo:
enum HTTPMethod {\n case get\n case post\n case put\n \n static let defaultMethod: HTTPMethod = .get\n}\n\nstruct AppConfig {\n static let apiURL = "https://api.example.com"\n static let timeout: Double = 30.0\n}\n\nAqui, static funciona perfeitamente, pois estruturas e enumerações não suportam herança e, portanto, não precisam de substituição.
Class: flexibilidade com herança
\nJá a palavra-chave class é uma ferramenta para quem trabalha com classes (e apenas com elas) e deseja permitir que subclasses substituam métodos ou propriedades. class não pode ser usado em estruturas ou enumerações.
Suponha que temos uma classe base Vehicle e queremos que cada subclasse possa fornecer sua própria versão da descrição:
class Vehicle {\n class func description() -> String {\n return "Isto é um veículo"\n }\n}\n\nclass Car: Vehicle {\n override class func description() -> String {\n return "Isto é um carro"\n }\n}\n\nclass Bicycle: Vehicle {\n override class func description() -> String {\n return "Isto é uma bicicleta"\n }\n}\n\nprint(Vehicle.description()) // Isto é um veículo\nprint(Car.description()) // Isto é um carro\nprint(Bicycle.description()) // Isto é uma bicic