Những Mẹo Lập Trình Với Objective-C Phần 2
Tiếp theo phần trước, trong bài viết này sẽ giới thiệu 1 kỹ thuật khác trong Objective C: Swizzling method.
Swizzling
Thông thường, khi muốn thêm vào 1 class có sẵn 1 vài hàm mới, chúng ta có thể dùng Categories
, đặc biệt là các class của thư viện (ko có source code) như NSArray, NSDictionary… Tuy nhiên, cách dùng Categories
có 1 hạn chế là bạn không thể override các hàm có sẵn. Vậy đây chính là lý do chúng ta cần sử dụng đến Swizzling method.
Trong Objective C, khi bạn viết 1 đoạn code
1
|
|
bạn không thực sự gọi đến hàm presentViewController:animated:completion:
mà thay vào đó là gửi đi 1 message presentViewController:animated:completion:
. Trong quá trình chạy, object sẽ tìm kiếm method tương ứng dựa vào id của message này. Chúng ta có thể dựa vào swizzling để thay đổi cách object tìm kiếm method tương ứng này:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Đi từng bước cho đoạn code ở trên:
Trước hết chúng ta tạo ra các selectors (SEL):
firstMethodSelector
vàsecondMethodSelector
Lấy ra các hàm tương ứng với selectors gán vào
firstMethod
vàsecondMethod
MethodThêm vào class định nghĩa của method thứ 2 dưới cách gọi của method thứ nhất. Trường hợp này xảy ra khi method thứ nhất không thực sự tồn tại (trong 1 khả năng nào đó)
Nếu điều này xảy ra, chúng ta cần 1 định nghĩa cho selector của method thứ 2, vì vậy thay thế nó bằng implementation của method thứ nhất (rỗng)
Nếu không xảy ra, nghĩa là method thứ nhất có tồn tại, chúng ta thay đổi implementation của 2 method.
Ví dụ 1
Khi sử dụng Google Analystics, chúng ta muốn track page view cho tất cả các UIViewController trong project, tuy nhiên, nếu ở class nào cũng gọi hàm trackView:<class_name>
thì tương đối nhiều, mà có thể còn bỏ sót. Vậy cách đơn giản nhất là override lại hàm viewDidLoad
của UIViewController
, trong đó chúng ta thực hiện trackView
hoặc gọi 1 hàm khác bất kỳ, tuỳ theo mục đích của mình.
Chúng ta viết phần code trên trong Categories
của NSObject
, từ đó có thể gọi nó từ bất kỳ class nào:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
Bây giờ tạo tiếp Categories
cho UIViewController:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
Khi Objective-C run-time load 1 category, nó sẽ gọi đến hàm load
. Chúng ta sử dụng dispatch_once để chắc chắn rằng hàm swizzle chỉ được gọi 1 lần. Sau khi import category này, (tốt nhất là trong file prefix - pch) tất cả các hàm viewDidLoad
của UIViewController
sẽ được thay thế bằng hàm myViewDidLoad
.
Ví dụ 2
1 ứng dụng khác của swizzling method là khi debug lỗi index out of range
của NSArray. Nhiều khi gặp phải lỗi này nhưng chương trình không dừng lại ở đúng đoạn code bị lỗi (nhảy ra hàm main). 1 cách đơn giản để xử lý trường hợp này là override hàm objectAtIndex:
của NSArray và bắt exception trong đó. Tuy nhiên, cách sử dụng swizzling method ở đây có hơi khác 1 chút.
Trước hết là tạo Category
cho NSArray
:
1 2 3 4 5 6 7 8 9 10 |
|
Đặt 1 breakpoint vào trong điều kiện if (index >= self.count)
để có thể biết được lỗi đến từ đâu. Sau đó, trong hàm main
của main.m
, thực hiện exchange method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Lưu ý ở đây chúng ta gọi Class arrayClass = NSClassFromString(@"__NSArrayM");
là bởi vì hàm objectAtIndex:
không đến từ NSArray
class mà đến từ __NSArrayM
(xem trên console debug). Chính vì thế chúng ta không thể sử dụng cách swizzle thông thường như trong ví dụ 1.
Để test đoạn code này, trong 1 đoạn chương trình bất kỳ, tạo ra 1 bug:
1 2 |
|
Bây giờ, chạy chương trình và tận hưởng thành quả :)