2008-11-27 32 views

Trả lời

504

tôi sử dụng [NSException raise:format:] như sau:

[NSException raise:@"Invalid foo value" format:@"foo of %d is invalid", foo];
+9

Tôi thích cách này như được áp dụng cho phương pháp '@throw ([NSException exceptionWith…])' vì nó ngắn gọn hơn. –

+9

Hãy chắc chắn để đọc các caveat quan trọng từ tác hại (http://stackoverflow.com/questions/324284/324805#324805) –

+25

Tôi thường thích điều này quá, nhưng có một gotcha. Có thể chỉ là phiên bản Xcode hiện tại của tôi, nhưng cú pháp [NSException raise ...] dường như không được nhận dạng bởi trình phân tích cú pháp như một đường dẫn thoát khỏi một phương thức trả về một giá trị. Tôi thấy cảnh báo "Kiểm soát có thể kết thúc chức năng không có hiệu lực" khi sử dụng cú pháp này, nhưng với cú pháp @throw ([NSException exceptionWith…]), trình phân tích cú pháp nhận ra đó là lối thoát và không hiển thị cảnh báo. – mattorb

57
@throw([NSException exceptionWith…]) 
+1

hey peter, lợi ích giữa cái này và eJames là gì? –

+11

@Steph Thirion: Xem http://developer.apple.com/documentation/Cocoa/Conceptual/Exceptions/Tasks/RaisingExceptions.html cho tất cả các chi tiết. Tóm lại? Cả hai sẽ làm việc, nhưng @throw có thể được sử dụng để ném các đối tượng không thuộc lớp NSException. –

+9

@StephThirion Ngoài câu trả lời của e.James, một điểm quan trọng là Xcode nhận ra các câu lệnh '@ throw' như các điểm thoát hàm, giống như câu lệnh' return'. Sử dụng e.Giải pháp của James, bạn có thể nhận được các cảnh báo "Điều khiển có thể đến hết chức năng không có hiệu lực", mà cú pháp '@ throw' tránh được. Tôi thích giải pháp này cho người được chấp nhận bởi e.James vì ​​lý do đó thôi. –

14

Tôi nghĩ rằng để được nhất quán nó đẹp hơn để sử dụng @throw với lớp của riêng bạn mà kéo dài NSException. Sau đó, bạn sử dụng các ký hiệu tương tự cho cố gắng nắm bắt cuối cùng:

@try { 
..... 
} 
@catch{ 
... 
} 
@finally{ 
... 
} 

Apple giải thích ở đây làm thế nào để ném và xử lý các trường hợp ngoại lệ: Catching Exceptions Throwing Exceptions

249

Một lời cảnh báo ở đây. Trong mục tiêu-C, không giống như nhiều ngôn ngữ tương tự, bạn thường nên cố gắng tránh sử dụng các ngoại lệ cho các tình huống lỗi phổ biến có thể xảy ra trong hoạt động bình thường.

Apple's documentation for Obj-C 2.0 tiểu bang như sau: "Chú ý: Trường hợp ngoại lệ là tài nguyên-chuyên sâu trong Objective-C Bạn không nên sử dụng các ngoại lệ cho điều khiển luồng chung, hoặc chỉ đơn giản là để biểu thị lỗi (chẳng hạn như một tập tin không được truy cập)."

Apple's conceptual Exception handling documentation giải thích tương tự, nhưng với nhiều từ hơn: "Quan trọng: Bạn nên đặt trước việc sử dụng ngoại lệ cho lập trình hoặc lỗi thời gian chạy không mong muốn như truy cập bộ sưu tập ngoài giới hạn, cố gắng thay đổi đối tượng không thay đổi, gửi thư không hợp lệ và mất kết nối với máy chủ cửa sổ. Bạn thường xử lý các loại lỗi này với các ngoại lệ khi một ứng dụng đang được tạo chứ không phải lúc chạy. [.....] Thay vì ngoại lệ, các đối tượng lỗi (NSError) và lỗi Cocoa cơ chế phân phối là cách được khuyến nghị để truyền đạt lỗi dự kiến ​​trong ứng dụng Cocoa. "

Lý do cho việc này một phần là tuân thủ các thành ngữ lập trình trong Mục tiêu-C (sử dụng giá trị trả về trong trường hợp đơn giản và tham số tham chiếu (thường là lớp NSError) trong trường hợp phức tạp hơn) đắt hơn và cuối cùng (và perpaps quan trọng nhất) rằng trường hợp ngoại lệ Objective-C là một wrapper mỏng xung quanh C của setjmp() và longjmp() chức năng, về cơ bản rối tung lên xử lý bộ nhớ cẩn thận của bạn, xem this explanation.

+11

Tôi nghĩ rằng điều này áp dụng cho hầu hết các ngôn ngữ lập trình: "cố gắng tránh sử dụng ngoại lệ cho các tình huống lỗi phổ biến". Điều tương tự cũng áp dụng trong Java; thực hành không tốt để xử lý các lỗi nhập của người dùng (ví dụ) với các ngoại lệ. Không chỉ vì sử dụng tài nguyên, mà còn cho mã rõ ràng. – beetstra

+6

Quan trọng hơn, ngoại lệ trong ca cao được thiết kế để chỉ ra các lỗi chương trình không thể phục hồi. Làm cách khác là chạy chống lại khuôn khổ và có thể dẫn đến hành vi không xác định. Xem http://stackoverflow.com/questions/3378696/iphone-try-end-try/3379240#3379240 để biết chi tiết. – KPM

+8

"Điều tương tự cũng áp dụng trong Java;" Không đồng ý. Bạn có thể sử dụng các ngoại lệ đã kiểm tra trong Java tốt cho các điều kiện lỗi bình thường. Tất nhiên bạn sẽ không sử dụng các ngoại lệ Runtime. – Zammbi

33

Tôi không có đại diện để nhận xét về phản hồi của eJames, vì vậy tôi đoán tôi cần phải đưa tôi vào đây. Đối với những người đến từ một nền Java, bạn sẽ nhớ lại rằng Java phân biệt giữa Exception và RuntimeException. Ngoại lệ là một ngoại lệ được kiểm tra, và RuntimeException được bỏ chọn. Cụ thể, Java đề xuất sử dụng các ngoại lệ đã kiểm tra cho "các điều kiện lỗi bình thường" và các ngoại lệ không được kiểm tra cho "lỗi thời gian chạy do lỗi lập trình viên gây ra". Dường như các ngoại lệ Objective-C nên được sử dụng trong cùng một vị trí bạn sẽ sử dụng một ngoại lệ không được kiểm soát và các giá trị trả về mã lỗi hoặc các giá trị NSError được ưu tiên ở những nơi bạn sẽ sử dụng một ngoại lệ đã kiểm tra.

+1

Có điều này đúng (sau 4 năm mặc dù: D), tạo lỗi của riêng bạn lớp ABCError mở rộng từ lớp NSError và sử dụng nó cho các ngoại lệ được kiểm tra chứ không phải là các ngoại lệ NSExceptions. Nâng cao NSExceptions nơi xảy ra lỗi lập trình (tình huống bất ngờ như vấn đề định dạng số). – chathuram

3

Tôi tin rằng bạn không bao giờ nên sử dụng Ngoại lệ để kiểm soát luồng chương trình thông thường. Nhưng ngoại lệ nên được ném bất cứ khi nào một số giá trị không khớp với giá trị mong muốn.

Ví dụ, nếu một số chức năng chấp nhận một giá trị, và giá trị mà không bao giờ được phép có con số không, sau đó nó là tốt để TROW một ngoại lệ chứ không phải sau đó cố gắng để làm điều gì đó 'thông minh' ...

Ries

14

Kể từ ObjC 2.0, trường hợp ngoại lệ Objective-C là không còn là một wrapper cho setjmp C() longjmp(), và tương thích với C++ ngoại lệ, các @try là "miễn phí", nhưng ném và bắt ngoại lệ là cách tốn kém hơn.

Dù sao, xác nhận (sử dụng NSAssert và NSCAssert macro family) ném NSException, và sane để sử dụng chúng như là trạng thái Ries.

+0

Điều cần biết! Chúng tôi có một thư viện của bên thứ ba mà chúng tôi không muốn sửa đổi mà ném ngoại lệ cho ngay cả những lỗi nhỏ nhất. Chúng ta phải bắt chúng ở một nơi trong ứng dụng và nó chỉ làm cho chúng ta co rúm lại, nhưng điều này làm cho tôi cảm thấy tốt hơn một chút. –

-7

Không có lý do không sử dụng ngoại lệ bình thường trong quan C thậm chí để biểu thị trường hợp ngoại lệ quy tắc kinh doanh. Apple có thể nói sử dụng NSError người quan tâm. Obj C đã được khoảng một thời gian dài và tại một thời gian ALL C + + tài liệu cho biết điều tương tự. Lý do nó không vấn đề như thế nào đắt ném và bắt một ngoại lệ là, là cuộc đời của một ngoại lệ là cực kỳ ngắn và ... một EXCEPTION của mình cho dòng chảy bình thường. Tôi chưa bao giờ nghe bất cứ ai nói trong cuộc đời của tôi, người đàn ông ngoại lệ đó mất một thời gian dài để bị ném và bị bắt.

Ngoài ra, có những người nghĩ rằng mục tiêu C chính nó là quá đắt và mã trong C hoặc C++ thay thế. Vì vậy, nói rằng luôn luôn sử dụng NSError là không thông tin và hoang tưởng.

Nhưng câu hỏi của chủ đề này vẫn chưa được trả lời cách tốt nhất để ném một ngoại lệ. Các cách để trả về NSError là hiển nhiên.

Vậy là: [NSException raise: ... @throw [[NSException alloc] initWithName .... hoặc @throw [[MyCustomException ...?

tôi sử dụng kiểm tra/quy tắc không được kiểm soát ở đây hơi khác biệt so với ở trên.

Sự khác biệt thực sự giữa (sử dụng phép ẩn dụ java ở đây) được kiểm tra/bỏ chọn là quan trọng -> cho dù bạn có thể phục hồi từ ngoại lệ. Và bằng cách phục hồi tôi có nghĩa là không chỉ không sụp đổ.

Vì vậy, tôi sử dụng các lớp ngoại lệ tùy chỉnh với @throw cho các trường hợp ngoại lệ có thể phục hồi, vì khả năng tôi sẽ có một số phương pháp ứng dụng tìm các loại lỗi nhất định trong nhiều khối @catch. Ví dụ, nếu ứng dụng của tôi là một máy ATM, tôi sẽ có một khối @catch cho "WithdrawalRequestExceedsBalanceException" .

Tôi sử dụng NSException: nâng cao ngoại lệ thời gian chạy vì tôi không có cách nào để khôi phục ngoại lệ, ngoại trừ để bắt nó ở cấp cao hơn và ghi nhật ký. Và theres không có điểm trong việc tạo ra một lớp học tùy chỉnh cho điều đó.

Dù sao thì cũng như những gì tôi làm, nhưng nếu có cách tốt hơn, tương tự như tôi muốn biết nữa. Trong mã của riêng tôi, vì tôi đã ngừng mã hóa C a hella từ lâu rồi nên tôi không bao giờ trả về NSError ngay cả khi tôi được chuyển qua một API.

+4

Tôi khuyên bạn nên cố gắng lập trình một máy chủ có ngoại lệ như là một phần của các trường hợp lỗi bình thường trước khi đưa ra các câu lệnh tổng quát như "không có lý do gì để không sử dụng ngoại lệ bình thường trong mục tiêu C". Tin tưởng rằng nó hay không, có những lý do để viết các ứng dụng hiệu suất cao (hoặc ít nhất là một phần của ứng dụng) trong ObjC, và ném ngoại lệ bình thường nghiêm túc cản trở hiệu suất. – jbenet

+5

Có những lý do thực sự rất tốt tại sao không sử dụng ngoại lệ trong Cocoa. Xem câu trả lời của Bill Bumgarner tại đây để biết thêm: http://stackoverflow.com/questions/3378696/iphone-try-end-try/3379240#3379240. Anh ta biết anh ta đang nói gì (gợi ý: kiểm tra chủ nhân của anh ta). Ngoại lệ trong ca cao được coi là lỗi không thể khôi phục và có thể khiến hệ thống ở trạng thái không ổn định. NSError là cách để đi qua các lỗi chung xung quanh. –

+0

Ngoại lệ ** đặc biệt **. Lỗi quy tắc kinh doanh chắc chắn không đủ điều kiện. "Tìm và thiết kế mã ngoại lệ nặng có thể dẫn đến một chiến thắng hoàn hảo." MSDN qua http://www.codinghorror.com/blog/2004/10/creating-more-exception-exceptions.html –

6

Sử dụng NSError để giao tiếp lỗi thay vì ngoại lệ.

điểm nhanh về NSError:

  • NSError cho phép mã lỗi C phong cách (số nguyên) để xác định rõ nguyên nhân gốc rễ và hy vọng cho phép xử lý lỗi để khắc phục lỗi. Bạn có thể bọc mã lỗi từ các thư viện C như SQLite trong các cá thể NSError rất dễ dàng.

  • NSError cũng có lợi ích là một đối tượng và cung cấp cách mô tả lỗi chi tiết hơn với thành viên từ điển userInfo của nó.Tuy nhiên, tốt nhất của tất cả, NSError KHÔNG THỂ được ném để nó khuyến khích một cách tiếp cận chủ động hơn để xử lý lỗi, trái ngược với các ngôn ngữ khác mà chỉ đơn giản là ném khoai tây nóng hơn nữa và tiếp tục lên các cuộc gọi ngăn xếp tại thời điểm đó nó chỉ có thể được báo cáo cho người dùng và không được xử lý theo bất kỳ cách nào có ý nghĩa (không phải nếu bạn tin vào việc tuân thủ nguyên lý thông tin lớn nhất của OOP đang ẩn).

tham khảo Link: Reference

+0

Nhận xét này không trả lời câu hỏi. Có lẽ OP chỉ muốn sụp đổ ứng dụng để kiểm tra xem khung báo cáo sự cố có hoạt động như mong đợi hay không. – Simon

6

Bạn có thể sử dụng hai phương pháp để nâng cao ngoại lệ trong khối try catch

@throw[NSException exceptionWithName]; 

hoặc phương pháp thứ hai

NSException e; 
[e raise]; 
6

Đây là cách tôi đã học được nó từ "Hướng dẫn Ranch Big Nerd (ấn bản thứ 4)":

@throw [NSException exceptionWithName:@"Something is not right exception" 
           reason:@"Can't perform this operation because of this or that" 
          userInfo:nil]; 
0

Bạn chỉ nên ném ngoại lệ nếu bạn thấy mình trong tình huống cho biết lỗi lập trình và muốn ngừng chạy ứng dụng. Vì vậy, cách tốt nhất để ném ngoại lệ là sử dụng các macro NSAssert và NSParameterAssert, và đảm bảo rằng NS_BLOCK_ASSERTIONS không được định nghĩa.

0

Mẫu mã đối với trường hợp: @throw ([NSException exceptionWithName: ...

- (void)parseError:(NSError *)error 
     completionBlock:(void (^)(NSString *error))completionBlock { 


    NSString *resultString = [NSString new]; 

    @try { 

    NSData *errorData = [NSData dataWithData:error.userInfo[@"SomeKeyForData"]]; 

    if(!errorData.bytes) { 

     @throw([NSException exceptionWithName:@"<Set Yours exc. name: > Test Exc" reason:@"<Describe reason: > Doesn't contain data" userInfo:nil]); 
    } 


    NSDictionary *dictFromData = [NSJSONSerialization JSONObjectWithData:errorData 
                   options:NSJSONReadingAllowFragments 
                    error:&error]; 

    resultString = dictFromData[@"someKey"]; 
    ... 


} @catch (NSException *exception) { 

      NSLog(@"Caught Exception Name: %@", exception.name); 
      NSLog(@"Caught Exception Reason: %@", exception.reason); 

    resultString = exception.reason; 

} @finally { 

    completionBlock(resultString); 
} 

}

Sử dụng:

[self parseError:error completionBlock:^(NSString *error) { 
      NSLog(@"%@", error); 
     }]; 

Một use-case cao cấp hơn:

- (void)parseError:(NSError *)error completionBlock:(void (^)(NSString *error))completionBlock { 

NSString *resultString = [NSString new]; 

NSException* customNilException = [NSException exceptionWithName:@"NilException" 
                  reason:@"object is nil" 
                 userInfo:nil]; 

NSException* customNotNumberException = [NSException exceptionWithName:@"NotNumberException" 
                   reason:@"object is not a NSNumber" 
                   userInfo:nil]; 

@try { 

    NSData *errorData = [NSData dataWithData:error.userInfo[@"SomeKeyForData"]]; 

    if(!errorData.bytes) { 

     @throw([NSException exceptionWithName:@"<Set Yours exc. name: > Test Exc" reason:@"<Describe reason: > Doesn't contain data" userInfo:nil]); 
    } 


    NSDictionary *dictFromData = [NSJSONSerialization JSONObjectWithData:errorData 
                   options:NSJSONReadingAllowFragments 
                    error:&error]; 

    NSArray * array = dictFromData[@"someArrayKey"]; 

    for (NSInteger i=0; i < array.count; i++) { 

     id resultString = array[i]; 

     if (![resultString isKindOfClass:NSNumber.class]) { 

      [customNotNumberException raise]; // <====== HERE is just the same as: @throw customNotNumberException; 

      break; 

     } else if (!resultString){ 

      @throw customNilException;  // <====== 

      break; 
     } 

    } 

} @catch (SomeCustomException * sce) { 
    // most specific type 
    // handle exception ce 
    //... 
} @catch (CustomException * ce) { 
    // most specific type 
    // handle exception ce 
    //... 
} @catch (NSException *exception) { 
    // less specific type 

    // do whatever recovery is necessary at his level 
    //... 
    // rethrow the exception so it's handled at a higher level 

    @throw (SomeCustomException * customException); 

} @finally { 
    // perform tasks necessary whether exception occurred or not 

} 

}