Objective-C: bản chất của ngôn ngữ và vị trí của nó trong phát triển iOS

Tác giả: IT Sectr Đã đăng: 2026-02-09 Thời gian đọc: 8 phút

Objective-C là ngôn ngữ lập trình với cơ chế điều phối thông điệp động, được tạo ra bởi Brad Cox vào những năm 1980. Apple đã chọn Objective-C làm ngôn ngữ chính cho NeXTSTEP, và sau đó cho iOS SDK. Programming With Objective-C — giới thiệu về cú pháp thông điệp và quản lý bộ nhớ.

Những điểm chính

  • Objective-C — một siêu tập chặt chẽ của C với cú pháp thông điệp trong dấu ngoặc vuông và runtime động
  • Message passing — gọi phương thức qua [object selector:argument] với tìm kiếm triển khai động thông qua objc_msgSend
  • Categories — cơ chế thêm phương thức vào các lớp hiện có mà không cần kế thừa và không cần truy cập mã nguồn
  • Blocks — hàm ẩn danh với bao đóng, tiền thân của closure trong Swift
  • ARC vs MRC — đếm tham chiếu tự động và thủ công để quản lý bộ nhớ

Objective-C là gì?

Objective-C là ngôn ngữ lập trình mở rộng C với khả năng lập trình hướng đối tượng theo phong cách Smalltalk. Mã Objective-C được biên dịch qua LLVM hoặc GCC thành mã máy gốc, duy trì tương thích ngược hoàn toàn với C — mọi mã C đều hợp lệ trong Objective-C.

Apple đã mua lại NeXT (cùng với Objective-C) vào năm 1997. Ngôn ngữ này trở thành nền tảng của Cocoa và Cocoa Touch — các framework cho macOS và iOS. Từ năm 2014, Apple đã thúc đẩy Swift như một sự thay thế, nhưng Objective-C vẫn cực kỳ quan trọng đối với các dự án kế thừa và một số framework hệ thống. Theo Apple (WWDC 2024), khoảng 35% ứng dụng trong App Store vẫn chứa mã Objective-C.

Tính năng chính là runtime động. Không giống như Swift, nơi các lời gọi phương thức được giải quyết tại thời điểm biên dịch, Objective-C gửi thông điệp tại thời điểm chạy qua hàm objc_msgSend. Điều này cho phép ghi đè phương thức ngay lập tức (method swizzling), thêm lớp động và sử dụng forward invocation cho các selector không tồn tại.

Cú pháp: C + mở rộng hướng đối tượng

Mọi mã C đều hợp lệ trong Objective-C. Phần mở rộng hướng đối tượng thêm lớp (@interface/@implementation), category, giao thức (@protocol) và kiểu động (id). Các tệp có phần mở rộng .m (triển khai) và .h (tiêu đề).

Cú pháp thông điệp và điều phối động

Cú pháp thông điệp là điểm khác biệt chính giữa Objective-C và các ngôn ngữ giống C. Thay vì object.method(argument), sử dụng [object method:argument]. Mỗi thông điệp đi qua objc_msgSend, nơi động tìm kiếm triển khai phương thức trong runtime.

objective-c
// Cú pháp thông điệp với tham số có tên
NSString *greeting = [NSString stringWithFormat:@"Hello, %@", name];

// Thông điệp lồng nhau
NSArray *sortedArray = [[array sortedArrayUsingSelector:@selector(compare:)] copy];

// Kiểm tra kiểu qua introspection
if ([object isKindOfClass:[UIView class]]) {
    UIView *view = (UIView *)object;
    view.backgroundColor = [UIColor redColor];
}

Điều phối động cho phép ghi đè phương thức tại thời điểm chạy (method swizzling) — một kỹ thuật mạnh mẽ nhưng nguy hiểm. Ví dụ, các framework như AFNetworking và Aspects sử dụng swizzling để chặn các lời gọi URLSession. Apple cảnh báo: swizzling có thể phá vỡ các framework hệ thống nếu sử dụng không đúng cách.

Category và mở rộng lớp

Category là một tính năng độc đáo của Objective-C cho phép thêm phương thức vào các lớp hiện có (bao gồm các lớp hệ thống như NSString và UIView) mà không cần kế thừa và không cần truy cập mã nguồn. Category được khai báo qua @interface ClassName (CategoryName).

objective-c
// Tệp UIColor+Hex.h — category cho UIColor
@interface UIColor (Hex)
+ (instancetype)colorWithHex:NSUIntegerhex;
@end

// Tệp UIColor+Hex.m — triển khai
@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];
}
@end

Mở rộng (Extensions) (Class Extension) là một loại category đặc biệt được khai báo trong tệp .m mà không có tên: @interface ClassName (). Không giống như category, mở rộng có thể thêm không chỉ phương thức mà còn ivar (biến instance) và thuộc tính. Mở rộng được sử dụng để ẩn API nội bộ khỏi các mô-đun bên ngoài.

Block — hàm ẩn danh với bao đóng

Block là hàm ẩn danh trong Objective-C capture các biến từ phạm vi bên ngoài. Cú pháp block: ^(int x) { return x * 2; }. Block được sử dụng trong UIKit cho trình xử lý callback, trong Grand Central Dispatch cho các tác vụ bất đồng bộ và trong bộ sưu tập NSArray/NSDictionary cho các thao tác hàm.

objective-c
// Khai báo kiểu block
typedef void (^CompletionBlock)(BOOL success, NSError *error);

// Block làm tham số
- (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);
        }
    });
}

Quản lý bộ nhớ trong block là cực kỳ quan trọng. Block capture self với tham chiếu mạnh, tạo ra retain cycle khi sử dụng trực tiếp. Giải pháp là __weak typeof(self) weakSelf = self, sau đó kiểm tra bên trong block. Vấn đề này đã được giải quyết hoàn toàn trong Swift thông qua capture lists [weak self].

ARC và MRC: quản lý bộ nhớ

Objective-C đã phát triển từ MRC (Manual Reference Counting) lên ARC (Automatic Reference Counting). Trong MRC, nhà phát triển gọi thủ công retain (tăng bộ đếm), release (giảm) và autorelease (giải phóng trì hoãn). Lỗi trong các lời gọi này dẫn đến rò rỉ bộ nhớ hoặc treo do con trỏ lơ lửng.

Thao tácMRCARC
Tạo đối tượng[[Object alloc] init][[Object alloc] init]
Giữ tham chiếu[object retain]Tự động
Giải phóng[object release]Tự động
Tự động giải phóng[object autorelease]Tự động (không cần)
Tham chiếu yếu__weak không tồn tại__weak (nil tự động)

ARC được giới thiệu trong Xcode 4.2 và LLVM 3.0 (2011). Trình biên dịch tự động chèn retain/release tại thời điểm biên dịch bằng cách phân tích vòng đời đối tượng. ARC không phải là dọn rác — đó là phân tích tĩnh với các chèn tự động. ARC của Objective-C tương thích với ARC của Swift: cả hai đều sử dụng cùng một hệ thống đếm tham chiếu ở cấp runtime.

Tương tác với Swift qua Bridging Header

Sử dụng chung Objective-C và Swift trong một dự án là thực tế phổ biến cho các dự án kế thừa. Apple cung cấp Bridging Header — một tệp được tạo tự động qua đó Swift nhìn thấy các lớp Objective-C và Objective-C nhìn thấy các lớp Swift kế thừa từ NSObject.

objective-c
// ProjectName-Bridging-Header.h
// Swift tự động nhìn thấy các tiêu đề này
#import "LegacyManager.h"
#import "NetworkClient.h"
#import "DataStore.h"

Di chuyển từ Objective-C sang Swift là một quá trình dần dần. Các tệp mới được viết bằng Swift, các tệp cũ dần dần được tái cấu trúc. Bridging Header được tạo tự động khi thêm tệp Swift đầu tiên vào dự án Objective-C. Để hiển thị ngược (ObjC → Swift), Xcode tạo tệp <ProjectName>-Swift.h chứa các khai báo @interface cho các lớp Swift được đánh dấu bằng @objc.

swift
// Lớp Swift hiển thị từ Objective-C
@objc class SwiftRouter: NSObject {
    @objc func navigateToProfile(userId: Int) {
        // triển khai
    }
}

Giới hạn: kiểu giá trị của Swift (struct, enum) không hiển thị trực tiếp từ Objective-C — chúng cần được bọc trong một lớp với @objc. Generics của Swift có khả năng truy cập hạn chế từ Objective-C. Cách tiếp cận được khuyến nghị là viết mã mới bằng Swift và chỉ tái cấu trúc mã Objective-C hiện có khi thay đổi chức năng tương ứng.

Runtime và Method Swizzling trong Objective-C

Lợi thế chính của Objective-C so với Swift là truy cập đầy đủ vào runtime. Method swizzling, thay thế triển khai phương thức tại thời điểm chạy, cho phép ghi đè phương thức hệ thống mà không cần kế thừa. Các thư viện như Aspects và JRSwizzle sử dụng khả năng này cho AOP (lập trình hướng khía cạnh) — giám sát, phân tích và ghi nhật ký.

Forward invocation là một tính năng runtime khác: nếu một đối tượng không phản hồi selector, hệ thống gọi forwardInvocation:, cho phép chuyển tiếp thông điệp đến đối tượng khác. Đây là nền tảng của mẫu Proxy trong Objective-C (NSProxy), được sử dụng cho khởi tạo lười biếng và đối tượng phân tán.

Apple khuyến nghị giảm thiểu việc sử dụng các thủ thuật runtime trong mã mới, ưu tiên kiểu tĩnh của Swift. Tuy nhiên, trong các dự án kế thừa, kiến thức về objc_msgSend, method_exchangeImplementations và objc_getAssociatedObject là cần thiết để duy trì cơ sở mã hiện có.

Thuộc tính và Key-Value Observing

Objective-C hỗ trợ thuộc tính như đường cú pháp trên ivar với getter/setter. Các bổ từ atomic/nonatomic, strong/weak/copy, readonly/readwrite và assign/retain xác định hành vi bộ nhớ và an toàn luồng. Key-Value Observing (KVO) là cơ chế quan sát thay đổi thuộc tính, được tích hợp trong runtime: bất kỳ đối tượng nào cũng có thể đăng ký thay đổi thuộc tính của đối tượng khác qua addObserver.

Câu hỏi thường gặp

Cú pháp thông điệp trong Objective-C là gì?

Một lời gọi phương thức trông giống [object selector:argument]. Thông điệp đi qua objc_msgSend, nơi động tìm triển khai trong runtime. Selector là tên phương thức (@selector(methodName)), không phải con trỏ hàm. Điều này cho phép swizzling và forward invocation.

Category khác với Extension như thế nào?

Category (@interface ClassName (Name)) thêm phương thức vào bất kỳ lớp nào (bao gồm lớp hệ thống) mà không cần kế thừa. Extension (@interface ClassName ()) được khai báo trong tệp .m và có thể thêm thuộc tính và ivars. Category không thể thêm ivars, nhưng Extension có thể.

Block trong Objective-C là gì?

Block là hàm ẩn danh với cú pháp ^(tham số) { thân }, capture biến từ ngữ cảnh. Chúng tương tự lambda trong C++ và closure trong Swift. Chúng yêu cầu __weak để ngăn retain cycles khi capture self. Được sử dụng trong UIKit, GCD và Foundation.

Sự khác biệt giữa ARC và MRC là gì?

MRC là đếm tham chiếu thủ công: lập trình viên gọi retain, release, autorelease. ARC là tự động: trình biên dịch chèn retain/release dựa trên phân tích tĩnh. ARC không phải là GC — đối tượng được giải phóng ngay khi bộ đếm về không. ARC hỗ trợ bổ từ __weak và __strong.

Có thể sử dụng Objective-C và Swift trong cùng một dự án không?

Có, qua Bridging Header. Swift nhìn thấy Objective-C qua tiêu đề này, Objective-C nhìn thấy Swift qua <ProjectName>-Swift.h. Các lớp Swift phải kế thừa từ NSObject và được đánh dấu bằng @objc. Struct và enum của Swift không hiển thị trực tiếp với Objective-C.

Tổng kết

  • Objective-C — siêu tập chặt chẽ của C với runtime động và cú pháp thông điệp cho Cocoa/Cocoa Touch
  • Message passing — lời gọi qua [object method] với điều phối động qua objc_msgSend và khả năng swizzling
  • Category — thêm phương thức vào lớp hiện có mà không cần kế thừa, bao gồm lớp hệ thống của Apple
  • Block — hàm ẩn danh với bao đóng, yêu cầu __weak để ngăn retain cycles
  • ARC vs MRC — tiến hóa từ retain/release thủ công đến đếm tham chiếu tự động qua trình biên dịch
  • Bridging Header — cơ chế để Swift và Objective-C làm việc cùng nhau trong một dự án để di chuyển dần dần
  • Tầm quan trọng kế thừa — khoảng 35% ứng dụng App Store chứa mã Objective-C cần bảo trì

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm