Objective-C è un linguaggio di programmazione con dispatch dinamico dei messaggi, creato da Brad Cox negli anni '80. Apple ha scelto Objective-C come linguaggio principale per NeXTSTEP e successivamente per l'iOS SDK. Programming With Objective-C — un'introduzione alla sintassi dei messaggi e alla gestione della memoria.
Punti chiave
Objective-C è un linguaggio di programmazione che estende C con capacità di programmazione orientata agli object in stile Smalltalk. Il codice Objective-C viene compilato tramite LLVM o GCC in codice macchina nativo, mantenendo la piena compatibilità all'indietro con C — qualsiasi codice C è valido in Objective-C.
Apple ha acquisito NeXT (insieme a Objective-C) nel 1997. Il linguaggio è diventato la base di Cocoa e Cocoa Touch — i framework per macOS e iOS. Dal 2014, Apple promuove Swift come sostituto, ma Objective-C rimane criticamente importante per progetti legacy e alcuni framework di sistema. Secondo Apple (WWDC 2024), circa il 35% delle app nell'App Store contiene ancora codice Objective-C.
La caratteristica principale è il runtime dinamico. A differenza di Swift, dove le chiamate ai metodi vengono risolte in fase di compilazione, Objective-C invia messaggi in fase di esecuzione attraverso la funzione objc_msgSend. Ciò consente di sovrascrivere metodi al volo (method swizzling), aggiungere classi dinamicamente e utilizzare forward invocation per selettori inesistenti.
Qualsiasi codice C è valido in Objective-C. L'estensione orientata agli oggetti aggiunge classi (@interface/@implementation), categorie, protocolli (@protocol) e tipizzazione dinamica (id). I file hanno estensioni .m (implementazione) e .h (intestazioni).
La sintassi dei messaggi è la differenza principale tra Objective-C e i linguaggi simili a C. Invece di object.method(argument), si usa [object method:argument]. Ogni messaggio passa attraverso objc_msgSend, che cerca dinamicamente l'implementazione del metodo nel runtime.
// Sintassi dei messaggi con parametri nominati
NSString *greeting = [NSString stringWithFormat:@"Hello, %@", name];
// Messaggi annidati
NSArray *sortedArray = [[array sortedArrayUsingSelector:@selector(compare:)] copy];
// Controllo del tipo tramite introspection
if ([object isKindOfClass:[UIView class]]) {
UIView *view = (UIView *)object;
view.backgroundColor = [UIColor redColor];
}Il dispatch dinamico consente di sovrascrivere metodi in fase di esecuzione (method swizzling) — una tecnica potente ma pericolosa. Ad esempio, framework come AFNetworking e Aspects usano lo swizzling per intercettare le chiamate URLSession. Apple avverte: lo swizzling può rompere i framework di sistema se usato in modo errato.
Le Categorie sono una caratteristica unica di Objective-C che consente di aggiungere metodi a classi esistenti (incluse classi di sistema come NSString e UIView) senza ereditarietà e senza accesso al codice sorgente. Le categorie vengono dichiarate tramite @interface ClassName (CategoryName).
// File UIColor+Hex.h — categoria per UIColor
@interface UIColor (Hex)
+ (instancetype)colorWithHex:NSUIntegerhex;
@end
// File UIColor+Hex.m — implementazione
@implementation UIColor (Hex)
+ (instancetype)colorWithHex:NSUIntegerhex {
CGFloat r = ((hex >> 16) & 0xFF) / 255.0;
CGFloat g = ((hex >> 8) & 0xFF) / 255.0;
CGFloat b = (hex & 0xFF) / 255.0;
return [self colorWithRed:r green:g blue:b alpha:1.0];
}
@endLe Estensioni (Class Extension) sono un tipo speciale di categoria dichiarata in un file .m senza nome: @interface ClassName (). A differenza delle categorie, le estensioni possono aggiungere non solo metodi ma anche ivar (variabili di istanza) e proprietà. Le estensioni vengono utilizzate per nascondere l'API interna dai moduli esterni.
I Blocchi sono funzioni anonime in Objective-C che catturano variabili dall'ambito esterno. Sintassi dei blocchi: ^(int x) { return x * 2; }. I blocchi vengono utilizzati in UIKit per i gestori di callback, in Grand Central Dispatch per attività asincrone e nelle collezioni NSArray/NSDictionary per operazioni funzionali.
// Dichiarazione del tipo di blocco
typedef void (^CompletionBlock)(BOOL success, NSError *error);
// Blocco come parametro
- (void)fetchDataWithCompletion:(CompletionBlock)completion {
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
BOOL result = [weakSelf processData];
if (completion) {
completion(result, nil);
}
});
}La gestione della memoria nei blocchi è criticamente importante. I blocchi catturano self con un riferimento forte, creando un retain cycle se usati direttamente. La soluzione è __weak typeof(self) weakSelf = self, seguita da un controllo all'interno del blocco. Questo problema è completamente risolto in Swift tramite le capture lists [weak self].
Objective-C si è evoluto da MRC (Manual Reference Counting) ad ARC (Automatic Reference Counting). In MRC, lo sviluppatore chiamava manualmente retain (incrementare contatore), release (decrementare) e autorelease (rilascio differito). Errori in queste chiamate portavano a perdite di memoria o crash a causa di puntatori pendenti.
| Operazione | MRC | ARC |
|---|---|---|
| Creazione oggetto | [[Object alloc] init] | [[Object alloc] init] |
| Ritenzione riferimento | [object retain] | Automaticamente |
| Rilascio | [object release] | Automaticamente |
| Autorilascio | [object autorelease] | Automaticamente (non richiesto) |
| Riferimento debole | __weak non esisteva | __weak (nil automatico) |
ARC è stato introdotto in Xcode 4.2 e LLVM 3.0 (2011). Il compilatore inserisce automaticamente retain/release in fase di compilazione analizzando la vita degli oggetti. ARC non è garbage collection — è analisi statica con inserimenti automatici. L'ARC di Objective-C è compatibile con l'ARC di Swift: entrambi usano lo stesso sistema di conteggio dei riferimenti a livello di runtime.
L'uso congiunto di Objective-C e Swift in un unico progetto è una pratica comune per progetti legacy. Apple fornisce il Bridging Header — un file generato automaticamente attraverso il quale Swift vede le classi Objective-C e Objective-C vede le classi Swift che ereditano da NSObject.
// ProjectName-Bridging-Header.h
// Swift vede queste intestazioni automaticamente
#import "LegacyManager.h"
#import "NetworkClient.h"
#import "DataStore.h"La migrazione da Objective-C a Swift è un processo graduale. I nuovi file vengono scritti in Swift, i vecchi vengono gradualmente rifattorizzati. Il Bridging Header viene generato automaticamente quando si aggiunge il primo file Swift a un progetto Objective-C. Per la visibilità inversa (ObjC → Swift), Xcode genera un file <ProjectName>-Swift.h contenente dichiarazioni @interface per le classi Swift marcate con @objc.
// Classe Swift visibile da Objective-C
@objc class SwiftRouter: NSObject {
@objc func navigateToProfile(userId: Int) {
// implementazione
}
}Limitazioni: i tipi valore di Swift (struct, enum) non sono visibili direttamente da Objective-C — devono essere incapsulati in una classe con @objc. I generics di Swift hanno accessibilità limitata da Objective-C. L'approccio consigliato è scrivere nuovo codice in Swift e rifattorizzare il codice Objective-C esistente solo quando si modifica la funzionalità corrispondente.
Il principale vantaggio di Objective-C rispetto a Swift è l'accesso completo al runtime. Il method swizzling, che sostituisce le implementazioni dei metodi in fase di esecuzione, consente di sovrascrivere metodi di sistema senza ereditarietà. Librerie come Aspects e JRSwizzle usano questa capacità per AOP (programmazione orientata agli aspetti) — monitoraggio, analisi e registrazione.
Forward invocation è un'altra funzionalità del runtime: se un oggetto non risponde a un selettore, il sistema chiama forwardInvocation:, consentendo di inoltrare il messaggio a un altro oggetto. Questa è la base del pattern Proxy in Objective-C (NSProxy), utilizzato per inizializzazione pigra e oggetti distribuiti.
Apple raccomanda di minimizzare l'uso di trucchi del runtime nel nuovo codice, preferendo la tipizzazione statica di Swift. Tuttavia, in progetti legacy, la conoscenza di objc_msgSend, method_exchangeImplementations e objc_getAssociatedObject è necessaria per mantenere la base di codice esistente.
Objective-C supporta le proprietà come zucchero sintattico su ivar con getter/setter. I modificatori atomic/nonatomic, strong/weak/copy, readonly/readwrite e assign/retain determinano il comportamento della memoria e la sicurezza dei thread. Key-Value Observing (KVO) è un meccanismo per osservare i cambiamenti delle proprietà, integrato nel runtime: qualsiasi oggetto può sottoscriversi ai cambiamenti delle proprietà di un altro oggetto tramite addObserver.
Domande frequenti
Una chiamata a metodo appare come [object selector:argument]. Il messaggio passa attraverso objc_msgSend, che trova dinamicamente l'implementazione nel runtime. Un selettore è un nome di metodo (@selector(methodName)), non un puntatore a funzione. Ciò consente swizzling e forward invocation.
Una Categoria (@interface ClassName (Name)) aggiunge metodi a qualsiasi classe (incluse quelle di sistema) senza ereditarietà. Un'Estensione (@interface ClassName ()) viene dichiarata in un file .m e può aggiungere proprietà e ivar. Una Categoria non può aggiungere ivar, ma un'Estensione sì.
I Blocchi sono funzioni anonime con sintassi ^(parametri) { corpo }, che catturano variabili dal contesto. Sono analoghi alle lambda in C++ e alle closure in Swift. Richiedono __weak per prevenire retain cycle durante la cattura di self. Sono usati in UIKit, GCD e Foundation.
MRC è il conteggio manuale dei riferimenti: il programmatore chiama retain, release, autorelease. ARC è automatico: il compilatore inserisce retain/release basandosi sull'analisi statica. ARC non è GC — gli oggetti vengono rilasciati immediatamente quando il contatore raggiunge zero. ARC supporta i modificatori __weak e __strong.
Sì, tramite un Bridging Header. Swift vede Objective-C attraverso questa intestazione, Objective-C vede Swift tramite <ProjectName>-Swift.h. Le classi Swift devono ereditare da NSObject ed essere marcate con @objc. Gli struct e gli enum di Swift non sono direttamente visibili a Objective-C.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche