- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block;
Thông thường lý do sẽ rơi vào 1 vài trường hợp như sau:
- Khi bạn viết 1 Class quản lý việc truy xuất dữ liệu từ Database, các hàm truy xuất thường trả về là 1 mảng (NSArray hoặc NSMutableArray). Điều này rất là cơ bản và thường gặp. Nhưng đôi lúc chúng ta muốn viết dưới dạng có thể duyệt qua mảng ngay trong hàm gọi truy xuất (tương tự hàm enumerateObjectsUsingBlock của class NSArray như ví dụ trên).
- Khi bạn viết 1 Class quản lý việc gởi 1 request đến 1 URL của 1 API nào đó, nhận dữ liệu trả về, parse và trả về kết quả là 1 mảng có thể duyệt được.
- Muốn thay thế kiến trúc "Delegate" hoặc "NSNotification" mà chúng ta thường sử dụng để thông báo 1 process đã hoàn tất.
- Một lý do lớn khác nữa là sử dụng Block theo kiểu này sẽ giúp cho luồng code của bạn được sắp xếp theo 1 trật tự logic hơn nhiều.
Khởi tạo lớp quản lý việc download
Chúng ta sẽ tạo 1 Class để quản lý việc download tên là MyDownloader. Class này sẽ có 1 method tên là downloadPlistFromURL.
- (void) downloadPlistForURL:(NSURL *) url completionBlock:(void (^)(NSArray *data, NSError *error)) block;
Hãy cùng xem xét câu lệnh này. Đầu tiên, method này là 1 VOID (ko có return). Đó là do Block của chúng ta sẽ lo việc xử lý các tất cả data mà method này "trả về". Tham số đầu tiên của method (url) đơn giản chỉ là URL của file plist.
Kế tiếp, chúng ta truyền 1 block vào method. Hãy cùng xem xét cái Block này:
- "(void " - Từ khoá void cho biết Block ko return giá trị.
- "(^)" - Dấu ^ đứng 1 mình cho biết đó là 1 anonymous Block (ko có tên). Chúng ta sẽ nói về việc đặt tên Block ở phần sau.
- "(NSArray *data, NSError *error))" - Đây là các giá trị sẽ được trả ngược về Block khi nó được gọi. Có thể xem đây là "return values" của method thông thường.
- "block" - Đây đơn giản là tên của tham số truyền vào method. Có thể đặt tên tuỳ ý.
OK. Bây giờ chúng ta sẽ viết hoàn chỉnh nội dung của method:
- (void) downloadPlistForURL:(NSURL *) url completionBlock:(void (^)(NSArray *data, NSError *error)) block {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul), ^{
NSArray *returnArray = [NSArray arrayWithContentsOfURL:url];
if(returnArray) {
block(returnArray, nil);
} else {
NSError *error = [NSError errorWithDomain:@"plist_download_error" code:1
userInfo:[NSDictionary dictionaryWithObject:@"Can't fetch data"
forKey:NSLocalizedDescriptionKey]];
block(nil, error);
}
});
}
- Ưu điểm đầu tiên có thể thấy là đoạn code đơn giản hơn việc sử dụng Delegate rất nhiều. Và ở đây chúng ta sử dụng Nested Block với GCD, chúng ta không cần lo về việc phải tạo AutoRelease Pool nữa.
- Với việc sử dụng GCD như trên, chúng ta chạy đoạn code trên ở 1 thread khác. Lưu ý việc sử dụng Block ở đây đã hoàn toàn thay thế phương pháp sử dụng Delegate với phương thức performSelectorInBackground.
- Đây là 1 đoạn code cực kỳ gọn đối với việc download dữ liệu từ file plist trên 1 URL và trả về 1 NSArray. Trong thực tế, có thể chúng ta sẽ muốn sử dụng NSURLConnection hay ASIHTTPRequest hơn, nhưng cách thực hiện sẽ tương tự như vậy.
- Việc kiểm tra lỗi cũng rất đơn giản, chỉ cần xem có dữ liệu đã được download hay chưa.
- Chúng ta chỉ việc gọi method, nhận được dữ liệu trả về và xử lý ngay trong hàm gọi method, không cần implement protocol (đối với cách sử dụng Delegate), không cần bắt tín hiệu Notification (đối với cách sử dụng NSNotificationCenter).
Gọi Method Vừa Tạo
OK, bây giờ chúng ta sẽ sử dụng method đó như thế nào. Trong trường hợp ví dụ này, chúng ta có 1 "Navigation Based" project, với 1 property kiểu NSArray tên là pListData. Và chúng ta sẽ hiển thị nội dung của pListData lên UITableView.
Đầu tiên, chúng ta sẽ import class MyDownloader, và viết thêm hàm gọi trong method viewDidLoad như sau:
- (void)viewDidLoad {
[super viewDidLoad];
MyDownloader *downloader = [[[MyDownloader alloc] init] autorelease];
NSURL *url = [NSURL URLWithString:@"http://www.icodeblog.com/samples/block_test/block_test.plist"];
[downloader downloadPlistForURL:url completionBlock:^(NSArray *data, NSError *error) {
self.plistData = data;
if(!error) {
dispatch_sync(dispatch_get_main_queue(), ^(void) {
[self.tableView reloadData];
});
} else {
NSLog(@"error %@", error);
}
}];
}
(Bài viết có tham khảo nội dung từ website: icodeblog.com)
blocktest.zip |