2009-09-03 40 views

Trả lời

26

Sử dụng rộng rãi NSAssert() sẽ không biến ObjC thành Eiffel, nhưng nó vẫn là một thực hành khá tốt miễn là bạn ghi nhớ cách nó thực sự được triển khai và những gì nó đang thực hiện. Những điều cần ghi nhớ về NSAssert():

Xcode không tắt NSAssert() trong Chế độ phát hành theo mặc định. Bạn phải nhớ thêm NS_BLOCK_ASSERTIONS vào GCC_PREPROCESSOR_DEFINITIONS trong xcconfig. (Bạn là using xcconfigs, phải không?) Một vấn đề phổ biến là để khẳng định không nil trong trường hợp nil lặng lẽ sẽ làm việc; điều này có thể có nghĩa là trường bị treo cho những thứ có thể khôi phục một cách duyên dáng. Điều này không liên quan đến macro NDEBUG được sử dụng bởi assert() và bạn phải nhớ xác định cả hai nếu mã của bạn bao gồm cả hai loại xác nhận.

Nếu bạn biên dịch NSAssert() ở chế độ Phát hành, khi đó bạn sẽ không có chỉ báo nào xảy ra sự cố khi khách hàng gửi cho bạn nhật ký của họ. Cá nhân tôi quấn NSAssert() trong các macro của riêng tôi luôn đăng nhập, ngay cả trong chế độ Phát hành.

NSAssert() thường buộc logic trùng lặp. Hãy xem xét trường hợp thử nghiệm một con trỏ C++ cho NULL. Bạn sử dụng NSAssert(), nhưng bạn vẫn còn cũng cần sử dụng thử nghiệm if() đơn giản để tránh bị rơi trong trường. Loại mã trùng lặp này có thể trở thành nguồn gốc của các lỗi và tôi đã thấy mã không thành công do các xác nhận không còn hợp lệ nữa. (May mắn là điều này thường ở chế độ Debug!) Tôi đã tranh luận rất nhiều về cách tạo macro kết hợp xác nhận và if(), nhưng khó thực hiện mà không bị mong manh hoặc làm cho mã khó hiểu.

Vì vấn đề cuối cùng, tôi thường đặt "NSAssert(NO, ...)" trong mệnh đề else{} thay vì thực hiện xác nhận ở đầu phương thức. Đây không phải là lý tưởng bởi vì nó di chuyển hợp đồng ra khỏi chữ ký phương pháp (do đó làm giảm lợi ích tài liệu của nó), nhưng nó là phương pháp hiệu quả nhất tôi đã tìm thấy trong thực tế.

+0

Tôi không hiểu tại sao bạn sẽ kết thúc bằng mã trùng lặp. Nếu xác nhận không thành công, mã của bạn không có nghĩa vụ lưu chính nó khỏi sự cố. Trong thực tế không có điểm, bởi vì một giả định bạn đã thực hiện đã bị phá vỡ, do đó, không có cách lành mạnh để lưu chương trình không bị rơi sau này, do trạng thái không nhất quán. –

+0

Mã của bạn luôn có nghĩa vụ hoạt động tốt cho người dùng. Trong khi trong một số trường hợp, mối quan tâm của sự tham nhũng dữ liệu có nghĩa là việc đâm ngay lập tức là cách tiếp cận duy nhất. Nhưng trong nhiều trường hợp, điều này không đúng và khôi phục, cho thấy lỗi hoặc thậm chí không làm gì là thích hợp hơn trong Release vs crashing và để người dùng nhìn chằm chằm vào Springboard mà không có thông tin. Tôi đã nhìn thấy đủ xác nhận không chính xác trong mã sản xuất để rất cảnh giác với việc bị lỗi trước khi phát hành trong trường hợp không có tham nhũng dữ liệu có thể xảy ra. Gỡ lỗi là một tình huống hoàn toàn khác, và tôi ủng hộ sự cố nhanh chóng. –

+0

Một ứng dụng tốt thừa nhận rằng nó có lỗi và tìm cách chứa chúng. Chỉ vì tôi có một lỗi nhỏ trong hệ thống trợ giúp không có nghĩa là toàn bộ trò chơi sẽ bị lỗi chỉ vì kỹ thuật "chương trình giờ đây không được xác định". Mỗi chương trình thực sự của bất kỳ kích thước liên quan đến khá nhiều hành vi không xác định. Đó là trách nhiệm của chúng tôi để giảm thiểu điều đó, nhưng cũng để sống trong đó và không sụp đổ mỗi khi một cái gì đó trông lạ. –

7

Gỡ lỗi. Bất cứ khi nào bạn viết mã, bạn hầu như luôn đưa ra các giả định. Giả định về trạng thái của môi trường, giá trị của tham số, biến cục bộ và trường của bạn, v.v. Thông thường, những giả định này là sai (một đồng nghiệp cũ cho tôi một câu châm ngôn tốt, rằng "Giả định là mẹ của tất cả các fsckups") .

Các xác nhận tồn tại để xác thực giả định của bạn, tại thời điểm bạn tạo chúng. Bạn có phương thức

void foo(int x) 
{ 
    … 
} 

mà bạn biết và có tài liệu chỉ hoạt động cho x> 5? Khẳng định nó!

Các xác nhận trực tiếp cùng với thử nghiệm đơn vị và các phương pháp chính thức như một phần của thực hành mã hóa tốt. Trong khi một số có thể nghĩ rằng các bài kiểm tra đơn vị toàn diện đảm bảo các xác nhận là thừa, thì không phải như vậy. Ví dụ, bạn có thể có một giả định cho một phương thức mà, nói rằng, nhân viên luôn luôn trên 16 và dưới 100 tuổi - nhưng mã đó, vào lúc này, không yêu cầu điều này. Các bài kiểm tra đơn vị vượt qua các tham số đó sẽ thành công, nhưng sau đó khi bạn cần sử dụng giả định của mình, bạn sẽ có mã ở khắp mọi nơi đã vượt qua các bài kiểm tra, nhưng là sai.

9

Cảnh báo chính là các tác dụng phụ.Nếu bạn viết:

NSAssert([self.navigationController popViewControllerAnimated:YES]!=nil,@"Something fails"); 

popViewControllerAnimated: sẽ được thực hiện trong phiên bản debug, nhưng không phải trong phiên bản phát hành mà dải NSAssert(). Điều này có nghĩa là phiên bản phát hành của bạn sẽ hoạt động khác với phiên bản gỡ lỗi.

Vấn đề này sẽ biến mất nếu bạn là cẩn thận đủ:

UIViewController* vc = [self.navigationController popViewControllerAnimated:YES]; 
NSAssert(vc!=nil,@"Something fails"); 
+0

+1 sâu sắc, mặc dù tôi không bao giờ gọi phương thức trong xác nhận. –

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