2009-09-22 31 views
17

Hôm nay tôi đã thử nghiệm với khối Objective-C vì vậy tôi nghĩ rằng tôi muốn được thông minh và thêm vào NSArray một vài phương pháp thu thập chức năng theo phong cách mà tôi đã nhìn thấy trong các ngôn ngữ khác:Sử dụng Objective-C Khối

@interface NSArray (FunWithBlocks) 
- (NSArray *)collect:(id (^)(id obj))block; 
- (NSArray *)select:(BOOL (^)(id obj))block; 
- (NSArray *)flattenedArray; 
@end 

Phương pháp thu thập: phương thức lấy một khối được gọi cho mỗi mục trong mảng và dự kiến ​​trả về kết quả của một số thao tác sử dụng mục đó. Kết quả là tập hợp tất cả các kết quả đó. (Nếu khối trả về nil, không có gì được thêm vào tập kết quả.)

Phương thức select: sẽ trả về một mảng mới chỉ với các mục từ bản gốc, khi được chuyển làm đối số cho khối, khối được trả về VÂNG.

Và cuối cùng, phương thức flattenedArray lặp lại trên các mục của mảng. Nếu một mục là một mảng, nó đệ quy gọi flattenedArray trên nó và thêm kết quả vào tập kết quả. Nếu mục không phải là mảng, nó sẽ thêm mục vào tập kết quả. Tập kết quả được trả về khi mọi thứ kết thúc.

Vì vậy, bây giờ tôi đã có một số cơ sở hạ tầng, tôi cần một trường hợp thử nghiệm. Tôi quyết định tìm tất cả các tệp gói trong thư mục ứng dụng của hệ thống. Đây là những gì tôi đã đưa ra:

NSArray *packagePaths = [[[NSSearchPathForDirectoriesInDomains(NSAllApplicationsDirectory, NSAllDomainsMask, YES) collect:^(id path) { return (id)[[[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil] collect:^(id file) { return (id)[path stringByAppendingPathComponent:file]; }]; }] flattenedArray] select:^(id fullPath) { return [[NSWorkspace sharedWorkspace] isFilePackageAtPath:fullPath]; }]; 

Đúng - đó là tất cả một dòng và nó kinh khủng. Tôi đã thử một vài cách tiếp cận để thêm các dòng mới và thụt đầu dòng để cố gắng dọn dẹp nó, nhưng nó vẫn cảm thấy như thuật toán thực tế bị mất trong tất cả các nhiễu. Tôi không biết nếu nó chỉ là một điều cú pháp hoặc người thân của tôi trong kinh nghiệm với việc sử dụng một phong cách chức năng đó là vấn đề, mặc dù.

Để so sánh, tôi quyết định làm điều đó "theo cách cũ thời" và chỉ sử dụng các vòng lặp:

NSMutableArray *packagePaths = [NSMutableArray new]; 
for (NSString *searchPath in NSSearchPathForDirectoriesInDomains(NSAllApplicationsDirectory, NSAllDomainsMask, YES)) { 
    for (NSString *file in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:searchPath error:nil]) { 
     NSString *packagePath = [searchPath stringByAppendingPathComponent:file]; 
     if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:packagePath]) { 
      [packagePaths addObject:packagePath]; 
     } 
    } 
} 

IMO phiên bản này là dễ dàng hơn để viết và dễ đọc hơn để khởi động.

Tôi cho rằng có thể đây là một ví dụ xấu, nhưng có vẻ như đó là cách hợp pháp để sử dụng các khối đối với tôi. (Tôi có sai?) Tôi có thiếu một cái gì đó về cách viết hoặc cấu trúc mã Objective-C với các khối sẽ làm sạch điều này và làm cho nó rõ ràng hơn (hoặc thậm chí chỉ là rõ ràng như) phiên bản looped?

Trả lời

19

Sử dụng dòng mới và chia cuộc gọi của bạn trên nhiều dòng.

Mẫu chuẩn được sử dụng trên tất cả các API của Apple là phương thức hoặc chức năng chỉ nên thực hiện một đối số khối và đối số đó phải luôn là đối số cuối cùng.

Bạn đã làm gì. Tốt.

Bây giờ, khi viết code có sử dụng nói API, làm điều gì đó như:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSAllApplicationsDirectory, NSAllDomainsMask, YES); 
paths = [paths collect: ^(id path) { 
    ... 
}]; 
paths = [paths collect: ^(id path) { 
    ... 
}]; 
paths = [paths select: ^(id path) { 
    ... 
}]; 

Tức là thực hiện từng bước trong quá trình thu thập/chọn/lọc/làm phẳng/bản đồ/bất kỳ thứ gì như một bước riêng biệt. Điều này sẽ không nhanh hơn/chậm hơn so với các cuộc gọi phương thức xích.

Nếu bạn cần phải khối tổ ở bên của khối, sau đó làm như vậy với chư đầy đủ:

paths = [paths collect: ^(id path) { 
    ... 
    [someArray select:^(id path) { 
     ... 
    }]; 
}]; 

Cũng giống như lồng nhau nếu phát biểu hoặc những thứ tương tự. Khi nó quá phức tạp, hãy cấu trúc lại nó thành các hàm hoặc phương thức, nếu cần.

+1

đẹp giải pháp, mặc dù tôi không phải là một fan hâm mộ lớn của định nghĩa lại 'paths' nhiều lần. Tôi có lẽ sẽ kết thúc đặt tên một vài giá trị dựa trên nội dung của chúng và kết thúc với một giá trị được gọi là 'đường dẫn'. Chỉ cần hai xu của tôi. –

+0

Hoạt ảnh UIView sử dụng 2 khối. –

+0

Ông cho biết mô hình không phải là kinh điển. Những phương thức thực hiện nhiều khối tất cả đều có các khối như các tham số cuối cùng. Ngoài ra, các API đó đã xuất hiện trong iOS 4, đã lâu sau bài đăng này. – logancautrell

2

Tôi nghĩ rằng vấn đề là (trái với những gì các nhà phê bình của Python tuyên bố;) vấn đề không gian trắng. Trong một phong cách chức năng hơn, có vẻ như nó sẽ có ý nghĩa để sao chép phong cách của các ngôn ngữ chức năng khác. Cách viết LISP-y của bạn càng có thể giống như sau:

NSArray *packagePaths = [[[NSSearchPathForDirectoriesInDomains(NSAllApplicationsDirectory, NSAllDomainsMask, YES) 
          collect:^(id path) { 
             return [[[NSFileManager defaultManager] 
               contentsOfDirectoryAtPath:path 
                    error:nil] 
               collect:^(id file) { 
                 return [path stringByAppendingPathComponent:file]; 
                 } 
              ]; 
            } 
          ] 
          flattenedArray 
          ] 

          select:^(id fullPath) { 
            return [[NSWorkspace sharedWorkspace] isFilePackageAtPath:fullPath]; 
           } 
         ]; 

Tôi không nói điều này rõ ràng hơn phiên bản được lặp. Giống như bất kỳ công cụ nào khác, các khối là một công cụ và chúng chỉ nên được sử dụng khi chúng là công cụ thích hợp cho công việc. Nếu khả năng đọc bị lỗi, tôi muốn nói đó không phải là công cụ tốt nhất cho công việc. Các khối, sau đó, một bổ sung cho một ngôn ngữ cơ bản bắt buộc. Nếu bạn thực sự muốn sự đồng nhất của một ngôn ngữ chức năng, hãy sử dụng một ngôn ngữ chức năng.

Các vấn đề liên quan