2009-06-26 7 views
8

Tôi đang mã hóa các thói quen khác nhau và tôi đang cố gắng hết sức để giữ cho nó gọn gàng và được cấu trúc lại.Cấu trúc đúng để kiểm tra lỗi bằng NSError

Phương pháp Tôi đang tạo đang bắt đầu trông giống như mã này:

-(IBAction)buttonPress:(id)sender { 
    // Create Document Shopping List with this document 
    [self doSomething:&error]; 

    if(error) { 
     [NSApp presentError:&error]; 
     return nil; 
    } 

    [self doSomethingElse:&error]; 

    if(error) { 
     [NSApp presentError:&error]; 
     return nil; 
    } 

    [self doYetSomethingElse:&error]; 

    if(error) { 
     [NSApp presentError:&error]; 
     return nil; 
    } 
} 

Tôi yêu NSError, nhưng điều này có vẻ như một cách hết sức vụng về xử lý tất cả các lỗi của tôi.

Một vài suy nghĩ tôi đã có khoảng cách khác nhau:

a) kiểm tra lỗi có thể được xây dựng thành các phương pháp doSomething, doSomethingElse vv, nhưng sau đó tôi sẽ không thể để thoát khỏi phương pháp bấm nút mà không cần làm một số loại kiểm tra về giá trị trả về, điều này sẽ dẫn tôi trở lại một cấu trúc tương tự.

b) Tôi có thể thiết lập NSError như là giá trị quan trọng được quan sát, nhưng một cái gì đó về điều này cảm thấy sai lầm sâu sắc. Tôi rất ý thức về khả năng lạm dụng với KVO, vì vậy tôi đang cố gắng làm mọi thứ mà không có nó ở bất cứ đâu.

Chắc chắn tôi đang thiếu thứ gì đó thực sự cơ bản ở đây? Có một mô hình có thể giúp tôi không? Hoặc là cấu trúc này OK?

+0

Tôi hoàn toàn loạng choạng bởi phản hồi tuyệt vời mà tôi đã nhận được trong 10 phút qua! Thankyou tất cả rất nhiều cho câu trả lời của bạn. Rất, rất hữu ích. –

Trả lời

11

Tôi nghĩ rằng không có câu trả lời nào được trình bày ở đây có tính đến các thực tiễn được khuyến nghị của Apple.

Về cơ bản bạn không bao giờ nên kiểm tra đối tượng NSError để xác định xem có sự cố hay không. Bạn kiểm tra xem có vấn đề gì không bằng cách kiểm tra giá trị được trả về từ phương thức đưa con trỏ đến một con trỏ đến NSError.

Từ the Apple documentation:

quan trọng thành công hay thất bại được chỉ định bởi các giá trị trả về của phương pháp . Mặc dù phương pháp Cocoa rằng gián tiếp trở về đối tượng lỗi trong lỗi miền Cocoa được đảm bảo để trở về đối tượng như vậy nếu phương pháp thấy sự thất bại bằng cách trực tiếp trở về nil hoặc NO, bạn nên luôn kiểm tra giá trị trả về là nil hoặc NO trước khi cố gắng làm bất cứ điều gì với đối tượng NSError.

+0

Một số phương thức như void trả về sendAsynchronousRequest và điều duy nhất bạn có thể làm là kiểm tra NSError. Hay tôi đang thiếu một cái gì đó? –

+2

Trong trường hợp đó bạn kiểm tra các thông số khác. Đó là, NSURLResponse và NSData. –

0

Lưu ý: Khi tôi đăng bài này, tôi không biết về bất kỳ cuộc tranh luận ngoại lệ MacOS/Cocoa nào. Xin lưu ý điều này khi bạn xem xét câu trả lời này.

Tại sao không sử dụng exceptions thay thế? Điều này sẽ cho phép bạn phân phối thông số lỗi trên các phương thức của bạn và xử lý các lỗi của bạn ở một nơi.

-(IBAction)buttonPress:(id)sender { 
    // Create Document Shopping List with this document 
    @try { 
     [self doSomething]; 
     [self doSomethingElse]; 
     [self doYetSomethingElse]; 
    } @catch (NSException* e) { 
     [NSApp presentException:e]; 
    } 
    return nil; 
} 

Bạn sẽ tất nhiên cần phải ném ngoại lệ theo yêu cầu từ bên trong phương pháp doXXXX của bạn:

NSException* e = [NSException exceptionWithName:@"MyException" 
             reason:@"Something bad happened" 
             userInfo:nil]; 
@throw e; 
+3

Có một cuộc tranh luận đang diễn ra về việc sử dụng ngoại lệ cho điều khiển luồng có được chấp nhận trong Mac OS X/Cocoa hay không. Cá nhân tôi lấy vị trí (hầu hết) của Apple, ngoại lệ trong Cocoa là các lỗi lập trình đặc biệt và do đó không được sử dụng để kiểm soát luồng. – danielpunkass

+0

Sẽ được quan tâm để biết lý do tại sao điều này đã được đánh dấu xuống. Tôi đã bỏ lỡ điều gì? – teabot

+2

Ah, cảm ơn vì đã xóa nó lên @danielpunkass - Tôi đến từ một nền tảng Java, vì vậy vui vẻ sử dụng các ngoại lệ trong Objective-C không biết về một cuộc tranh luận như vậy. – teabot

1

Tôi nghĩ rằng câu hỏi thực sự là bao nhiêu chi tiết bạn cần trong việc xử lý lỗi của bạn. Mã của bạn về cơ bản là chính xác - nhưng bạn có thể làm sạch mọi thứ bằng cách kiểm tra lỗi ít thường xuyên hơn, hoặc sử dụng phương pháp tiếp cận toàn bộ như teabot được đề cập trong câu trả lời của mình với NSException. Rất nhiều hàm dựng sẵn trả về lỗi (như moveItemAtPath của NSFileManager) trả về một BOOL ngoài việc cung cấp một đối tượng NSError - vì vậy bạn cũng có thể sử dụng các câu lệnh if nếu bạn không cần thông tin NSError.

Tất cả và tất cả, thực sự không phải là cách tuyệt vời để thực hiện việc này. Nếu mã của bạn dài, KVO có thể là một giải pháp tuyệt vời. Tôi đã không bao giờ thử nó cho một tình huống như thế này, mặc dù.

+0

Cảm ơn vì bình luận, Ben. Lý tưởng nhất, tôi muốn có thể xử lý tất cả các lỗi một cách nhất quán - bằng cách sử dụng NSError. Tôi hơi bối rối bởi các chức năng mà đôi khi trả lại một BOOL - nếu có một cách tiêu chuẩn để xử lý lỗi bằng NSError, tại sao Apple sẽ làm bùn nước bằng cách cung cấp các giá trị trả về? Điều đó luôn làm tôi bối rối một chút. –

+1

Dựa vào một đối tượng NSError là, thẳng thắn, ngu ngốc. Nó rất dễ dàng cho một trong những bỏ lỡ rằng một trong những trường hợp mà tại thời điểm viết mã để tạo ra một đối tượng lỗi là quá nhiều rắc rối, và kết quả là, phương pháp báo cáo sai thành công. Đây là lý do tại sao Apple xử lý thông tin lỗi theo cách họ thực hiện. Sử dụng một thử nghiệm đơn giản của một phương thức trả về NO hoặc nil để xác định rằng nó không thành công. THEN thả xuống đối tượng NSError (nếu nó có sẵn) để biết thêm chi tiết. –

+0

Điểm rất tốt, Mike. Cung cấp cho tôi một cái gì đó khác để xem xét. Chỉ cần đọc này, tôi không phải kiểm tra SO kịp thời đủ. –

2

Ý chính của câu hỏi của bạn là liệu có những cải tiến về cấu trúc mà bạn có thể thực hiện đối với việc xử lý lỗi của mình hay không. Tôi nghĩ như vậy, về cơ bản giới thiệu nhiều lớp làm tổ, hoặc bằng cách trích xuất nhiều mã hơn vào các phương thức/hàm riêng biệt, hoặc bằng cách giới thiệu lồng nhau trong phương thức mẫu cao cấp của bạn.

Ý tưởng là, khi xử lý hầu hết các lỗi, bạn có thể quan tâm đến việc thực hiện tác vụ thay thế hoặc thất bại và truyền bá lỗi lên chuỗi sao cho một số bộ điều khiển có trách nhiệm có thể truyền tải lỗi đến người dùng Giao diện người dùng.

Sử dụng ý tưởng này "tuyên truyền hoặc xử lý", tôi sẽ viết lại phương pháp mẫu của bạn như thế này:

-(IBAction)buttonPress:(id)sender { 

    // Create Document Shopping List with this document 
    [self doSomething:&error];  
    if(error == nil) { 
     [self doSomethingElse:&error]; 
     if (error == nil) { 
      [self doYetSomethingElse:&error]; 
     } 
    } 

    if(error) { 
     [NSApp presentError:&error]; 
    }  
} 

Lưu ý rằng có những lý lẽ tốt chống lại giới thiệu quá nhiều làm tổ trong một phương pháp cụ thể. Làm tổ như thế này về bản chất là một thay thế ngắn cho phương pháp giải nén. Nó có thể có ý nghĩa hơn, ví dụ, rằng "doSomething:" chính nó gọi doSomethingElse :, gọi các hàm doYetSomethingElse: ví dụ. Điều này sẽ áp đặt cùng một cấu trúc trên mã như tổ hợp, nhưng sẽ được cho là dễ bảo trì hơn.

Là một sang một bên, tôi không phải là người hâm mộ các câu trả lời nội tuyến. Trong trường hợp cụ thể này, phương thức mẫu không thực sự gọi cho một giá trị trả về, nhưng nếu nó đã làm, tôi thích đặt một biến cục bộ cho giá trị trả lại và chỉ trả về ở cuối điều khiển luồng. Nhảy ra khỏi một chức năng hoặc phương pháp sớm là một cách chắc chắn để gặp lỗi lạ, IMHO.

+0

Cảm ơn một đề xuất tuyệt vời, Daniel. Tôi tránh xa việc này vì Code Complete khuyến nghị giảm thiểu lồng nhau. Tôi đã thực hiện một vài cơn ác mộng lồng nhau nếu .. khác báo cáo trong thời gian của tôi. Và nếu tôi có hơn 5 bước, tôi đoán điều này sẽ trở nên cồng kềnh. Mặt khác, nếu tôi có nhiều hơn 5 bước, tôi đoán đó sẽ là một dấu hiệu cảnh báo rằng mã của tôi cần phải được refactored anyway. Và điều này làm tổ cụ thể vẫn còn khá dễ đọc và là nhiều neater hơn bản gốc của tôi, vì vậy đây là những gì tôi sẽ làm. Cảm ơn bạn lần nữa! –

+1

Rất vui khi bạn đánh giá cao câu trả lời. Tôi cũng nghĩ về Code Complete và giảm thiểu lồng nhau, nhưng tôi hợp lý hóa nó theo cách tôi đã làm ở trên, để xem xét rằng lồng nhau tối thiểu chỉ là viết tắt của mã có thể được thêm vào một phương thức hoặc hàm khác khi hoàn cảnh gọi nó. Nói chung, tôi cho rằng lồng ghép là tốt hơn so với thay thế, một chức năng "chạy" ... – danielpunkass