Objective-C — γλώσσα προγραμματισμού με δυναμική αποστολή μηνυμάτων, που δημιουργήθηκε από τον Brad Cox τη δεκαετία του 1980. Η Apple επέλεξε το Objective-C ως κύρια γλώσσα για το NeXTSTEP και στη συνέχεια για το iOS SDK. Programming With Objective-C — εισαγωγή στη σύνταξη μηνυμάτων και τη διαχείριση μνήμης.
Κύρια σημεία
Objective-C — γλώσσα προγραμματισμού που επεκτείνει τη C με δυνατότητες αντικειμενοστρεφούς προγραμματισμού σε στυλ Smalltalk. Ο κώδικας Objective-C μεταγλωττίζεται μέσω LLVM ή GCC σε εγγενή κώδικα μηχανής, διατηρώντας πλήρη συμβατότητα προς τα πίσω με τη C — οποιοσδήποτε κώδικας C είναι σωστός στο Objective-C.
Η Apple εξαγόρασε τη NeXT (μαζί με το Objective-C) το 1997. Η γλώσσα έγινε η βάση των Cocoa και Cocoa Touch — των πλαισίων για macOS και iOS. Από το 2014, η Apple προωθεί τη Swift ως αντικατάσταση, αλλά το Objective-C παραμένει κρίσιμο για έργα κληρονομιάς και ορισμένα πλαίσια συστήματος. Σύμφωνα με την Apple (WWDC 2024), περίπου το 35% των εφαρμογών στο App Store εξακολουθούν να περιέχουν κώδικα Objective-C.
Βασικό χαρακτηριστικό — δυναμικό runtime. Σε αντίθεση με τη Swift, όπου οι κλήσεις μεθόδων επιλύονται στο στάδιο της μεταγλώττισης, το Objective-C στέλνει μηνύματα κατά την εκτέλεση μέσω της συνάρτησης objc_msgSend. Αυτό επιτρέπει την παράκαμψη μεθόδων εν κινήσει (method swizzling), τη δυναμική προσθήκη κλάσεων και τη χρήση forward invocation για ανύπαρκτους επιλογείς.
Οποιοσδήποτε κώδικας C είναι σωστός στο Objective-C. Το αντικειμενοστρεφές επίπεδο προσθέτει κλάσεις (@interface/@implementation), κατηγορίες, πρωτόκολλα (@protocol) και δυναμική τυποποίηση (id). Τα αρχεία έχουν καταλήξεις .m (υλοποίηση) και .h (κεφαλίδες).
Σύνταξη μηνυμάτων — η κύρια διαφορά του Objective-C από τις γλώσσες τύπου C. Αντί για object.method(argument) χρησιμοποιείται [object method:argument]. Κάθε μήνυμα περνά από το objc_msgSend, το οποίο αναζητά δυναμικά την υλοποίηση της μεθόδου στο runtime.
// Σύνταξη μηνυμάτων με ονομασμένες παραμέτρους
NSString *greeting = [NSString stringWithFormat:@"Hello, %@", name];
// Ένθετα μηνύματα
NSArray *sortedArray = [[array sortedArrayUsingSelector:@selector(compare:)] copy];
// Έλεγχος τύπου μέσω ενδοσκόπησης
if ([object isKindOfClass:[UIView class]]) {
UIView *view = (UIView *)object;
view.backgroundColor = [UIColor redColor];
}Dynamic dispatch επιτρέπει την παράκαμψη μεθόδων στο runtime (method swizzling) — μια ισχυρή αλλά επικίνδυνη τεχνική. Για παράδειγμα, πλαίσια όπως το AFNetworking και το Aspects χρησιμοποιούν swizzling για την υποκλοπή κλήσεων URLSession. Η Apple προειδοποιεί: το swizzling μπορεί να διαταράξει τη λειτουργία πλαισίων συστήματος εάν χρησιμοποιηθεί λανθασμένα.
Categories — μια μοναδική δυνατότητα του Objective-C να προσθέτει μεθόδους σε υπάρχουσες κλάσεις (συμπεριλαμβανομένων συστημικών, όπως NSString και UIView) χωρίς κληρονομικότητα και χωρίς πρόσβαση στον πηγαίο κώδικα. Οι κατηγορίες δηλώνονται μέσω @interface ClassName (CategoryName).
// Αρχείο UIColor+Hex.h — κατηγορία για UIColor
@interface UIColor (Hex)
+ (instancetype)colorWithHex:NSUIntegerhex;
@end
// Αρχείο UIColor+Hex.m — υλοποίηση
@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];
}
@endExtensions (Class Extension) — ένα ειδικό είδος κατηγορίας, που δηλώνεται σε αρχείο .m χωρίς όνομα: @interface ClassName (). Σε αντίθεση με τις κατηγορίες, τα extensions μπορούν να προσθέσουν όχι μόνο μεθόδους, αλλά και ivar (μεταβλητές στιγμιοτύπου) και property. Τα extensions χρησιμοποιούνται για την απόκρυψη εσωτερικών API από εξωτερικές ενότητες.
Blocks — ανώνυμες συναρτήσεις στο Objective-C που συλλαμβάνουν μεταβλητές από το εξωτερικό πεδίο ορατότητας. Σύνταξη μπλοκ: ^(int x) { return x * 2; }. Τα Blocks χρησιμοποιούνται στο UIKit για χειριστές callback, στο Grand Central Dispatch για ασύγχρονες εργασίες και στις συλλογές NSArray/NSDictionary για λειτουργικές πράξεις.
// Δήλωση τύπου μπλοκ
typedef void (^CompletionBlock)(BOOL success, NSError *error);
// Μπλοκ ως παράμετρος
- (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);
}
});
}Διαχείριση μνήμης στα μπλοκ είναι κρίσιμη. Τα μπλοκ συλλαμβάνουν το self με ισχυρή αναφορά, δημιουργώντας retain cycle κατά την άμεση χρήση. Λύση — __weak typeof(self) weakSelf = self, με επακόλουθο έλεγχο εντός του μπλοκ. Αυτό το πρόβλημα έχει λυθεί πλήρως στη Swift μέσω λιστών καταγραφής [weak self].
Το Objective-C έχει διανύσει το δρόμο από το MRC (Manual Reference Counting) στο ARC (Automatic Reference Counting). Στο MRC, ο προγραμματιστής καλούσε χειροκίνητα retain (αύξηση μετρητή), release (μείωση) και autorelease (καθυστερημένη αποδέσμευση). Σφάλματα σε αυτές τις κλήσεις οδηγούσαν σε διαρροές μνήμης ή καταρρεύσεις λόγω dangling pointers.
| Λειτουργία | MRC | ARC |
|---|---|---|
| Δημιουργία αντικειμένου | [[Object alloc] init] | [[Object alloc] init] |
| Διατήρηση αναφοράς | [object retain] | Αυτόματα |
| Αποδέσμευση | [object release] | Αυτόματα |
| Αυτόματη αποδέσμευση | [object autorelease] | Αυτόματα (δεν απαιτείται) |
| Weak αναφορά | __weak δεν υπήρχε | __weak (αυτόματο nil) |
Το ARC εισήχθη στο Xcode 4.2 και LLVM 3.0 (2011). Ο μεταγλωττιστής εισάγει αυτόματα retain/release στο στάδιο της μεταγλώττισης, αναλύοντας τη διάρκεια ζωής των αντικειμένων. Το ARC δεν είναι συλλογή απορριμμάτων (garbage collection) — είναι στατική ανάλυση με αυτόματες εισαγωγές. Το Objective-C ARC είναι συμβατό με το Swift ARC: και τα δύο χρησιμοποιούν το ίδιο σύστημα μέτρησης αναφορών σε επίπεδο runtime.
Η κοινή χρήση Objective-C και Swift σε ένα έργο — συνήθης πρακτική για έργα κληρονομιάς. Η Apple παρέχει το Bridging Header — ένα αυτόματα παραγόμενο αρχείο μέσω του οποίου η Swift βλέπει κλάσεις Objective-C και το Objective-C βλέπει κλάσεις Swift που κληρονομούν από το NSObject.
// ProjectName-Bridging-Header.h
// Η Swift βλέπει αυτές τις κεφαλίδες αυτόματα
#import "LegacyManager.h"
#import "NetworkClient.h"
#import "DataStore.h"Μετεγκατάσταση από Objective-C σε Swift — σταδιακή διαδικασία. Νέα αρχεία γράφονται σε Swift, παλιά αναδιαρθρώνονται σταδιακά. Το Bridging Header δημιουργείται αυτόματα κατά την προσθήκη του πρώτου αρχείου Swift σε ένα έργο Objective-C. Για αντίστροφη ορατότητα (ObjC → Swift), το Xcode δημιουργεί το αρχείο
// Κλάση Swift ορατή από Objective-C
@objc class SwiftRouter: NSObject {
@objc func navigateToProfile(userId: Int) {
// υλοποίηση
}
}Περιορισμοί: οι τύποι τιμών Swift (struct, enum) δεν είναι άμεσα ορατοί από το Objective-C — πρέπει να τυλιχτούν σε class με @objc. Τα Generics στη Swift είναι περιορισμένης πρόσβασης από το Objective-C. Συνιστώμενη προσέγγιση — γράψτε νέο κώδικα σε Swift και αναδιαρθρώστε τον υπάρχοντα κώδικα Objective-C μόνο όταν αλλάζει η σχετική λειτουργικότητα.
Το κύριο πλεονέκτημα του Objective-C έναντι της Swift — πλήρης πρόσβαση στο runtime. Το method swizzling, η αντικατάσταση υλοποιήσεων μεθόδων κατά την εκτέλεση, επιτρέπει την παράκαμψη συστημικών μεθόδων χωρίς κληρονομικότητα. Βιβλιοθήκες όπως το Aspects και το JRSwizzle χρησιμοποιούν αυτή τη δυνατότητα για AOP (προγραμματισμός προσανατολισμένος σε όψεις) — παρακολούθηση, αναλυτική και καταγραφή.
Forward invocation — μια άλλη δυνατότητα runtime: εάν ένα αντικείμενο δεν ανταποκρίνεται σε έναν επιλογέα, το σύστημα καλεί forwardInvocation:, επιτρέποντας την ανακατεύθυνση του μηνύματος σε άλλο αντικείμενο. Αυτή είναι η βάση του προτύπου Proxy στο Objective-C (NSProxy), που χρησιμοποιείται για καθυστερημένη αρχικοποίηση και κατανεμημένα αντικείμενα.
Η Apple συνιστά να ελαχιστοποιείται η χρήση τεχνασμάτων runtime σε νέο κώδικα, προτιμώντας τη στατική τυποποίηση της Swift. Ωστόσο, σε έργα κληρονομιάς, η γνώση των objc_msgSend, method_exchangeImplementations και objc_getAssociatedObject είναι απαραίτητη για τη συντήρηση της υπάρχουσας βάσης κώδικα.
Το Objective-C υποστηρίζει property ως συντακτική ζάχαρη πάνω από ivar με getters/setters. Οι τροποποιητές atomic/nonatomic, strong/weak/copy, readonly/readwrite και assign/retain καθορίζουν τη συμπεριφορά μνήμης και την ασφάλεια νημάτων. Το Key-Value Observing (KVO) — μηχανισμός παρακολούθησης αλλαγών ιδιοτήτων ενσωματωμένος στο runtime: οποιοδήποτε αντικείμενο μπορεί να εγγραφεί σε αλλαγές ιδιοτήτων άλλου αντικειμένου μέσω addObserver.
Συχνές ερωτήσεις
Η κλήση μεθόδου μοιάζει με [object selector:argument]. Το μήνυμα περνά από το objc_msgSend, το οποίο βρίσκει δυναμικά την υλοποίηση στο runtime. Ο επιλογέας είναι το όνομα της μεθόδου (@selector(methodName)), όχι δείκτης συνάρτησης. Αυτό επιτρέπει swizzling και forward invocation.
Η Category (@interface ClassName (Name)) προσθέτει μεθόδους σε οποιαδήποτε κλάση (συμπεριλαμβανομένων συστημικών) χωρίς κληρονομικότητα. Η Extension (@interface ClassName ()) δηλώνεται σε αρχείο .m και μπορεί να προσθέσει property και ivar. Η Category δεν μπορεί να προσθέσει ivar, η Extension μπορεί.
Blocks — ανώνυμες συναρτήσεις με σύνταξη ^(παράμετροι) { σώμα }, που συλλαμβάνουν μεταβλητές από το περιβάλλον. Ανάλογο με lambdas στη C++ και closures στη Swift. Απαιτούν __weak για αποτροπή retain cycles κατά τη σύλληψη του self. Χρησιμοποιούνται σε UIKit, GCD και Foundation.
MRC — χειροκίνητη μέτρηση αναφορών: ο προγραμματιστής καλεί retain, release, autorelease. ARC — αυτόματη: ο μεταγλωττιστής εισάγει retain/release βάσει στατικής ανάλυσης. Το ARC δεν είναι GC — τα αντικείμενα αποδεσμεύονται αμέσως όταν ο μετρητής μηδενιστεί. Το ARC υποστηρίζει τροποποιητές __weak και __strong.
Ναι, μέσω Bridging Header. Η Swift βλέπει το Objective-C μέσω αυτής της κεφαλίδας, το Objective-C βλέπει τη Swift μέσω
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης