2009-12-14 11 views
49

tôi đang ủng hộ 10.4+ bằng cách chọn API nhiều nhất hiện nay trong thời gian chạy:Đàn áp " '...' bị phản đối" khi sử dụng respondsToSelector

if ([fileManager respondsToSelector:@selector(removeItemAtPath:error:)]) 
    [fileManager removeItemAtPath:downloadDir error:NULL]; 
else 
    [fileManager removeFileAtPath:downloadDir handler:nil]; 

Trong trường hợp này, 10.5 và lên sẽ sử dụng removeItemAtPath:error: và 10.4 sẽ sử dụng removeFileAtPath:handler:. Tuyệt vời, nhưng tôi vẫn nhận được cảnh báo trình biên dịch cho các phương pháp cũ:

warning: 'removeFileAtPath:handler:' is deprecated [-Wdeprecated-declarations] 

Có một cú pháp của if([… respondsToSelector:@selector(…)]){ … } else { … } rằng gợi ý trình biên dịch (Clang) để không cảnh báo trên dòng đó?

Nếu không, có cách nào gắn thẻ dòng đó bị bỏ qua cho -Wdeprecated-declarations không?


Sau khi xem một số câu trả lời, hãy để tôi làm rõ rằng làm cho trình biên dịch hiểu rằng những gì tôi đang làm không phải là giải pháp hợp lệ.

Trả lời

104

tôi thấy an example trong tay người dùng của Clang trình biên dịch cho phép tôi bỏ qua những cảnh báo:

if ([fileManager respondsToSelector:@selector(removeItemAtPath:error:)]) { 
    [fileManager removeItemAtPath:downloadDir error:NULL]; 
} else { 
#pragma clang diagnostic push 
#pragma clang diagnostic ignored "-Wdeprecated-declarations" 
    [fileManager removeFileAtPath:downloadDir handler:nil]; 
#pragma clang diagnostic pop 
} 
+0

Bạn có thể kết thúc cuộc gọi trong '#pragma clang diagnostic ignore" -Wdeprecated-declarationations "' và sau đó '#pragma clang diagnostic warning" -Wdeprecated-declarationations "' nếu Clang trên OS X không hỗ trợ 'push' ? – Wevah

+0

+1 Đây là giải pháp tốt nhất! – trojanfoe

+0

@sidnicious bạn có thể cho tôi biết cách biết tên cụ thể của cảnh báo hay không. Ý tôi là, nó cho thấy cảnh báo " bị xóa" nhưng chúng tôi sử dụng "-Wdrecrecated-declarationations". Làm thế nào để tìm thấy tên atual chúng ta cần phải sử dụng trong chẩn đoán clang – Johnykutty

8

Bạn có thể khai báo một tập tin riêng biệt mà được thiết kế để gọi các phương pháp phản đối và thiết lập các cờ cho mỗi tập tin biên dịch trong Xcode để bỏ qua -Wdeprecated-declarations. Sau đó, bạn có thể xác định hàm giả trong tệp đó để gọi các phương thức không được chấp nhận và do đó tránh các cảnh báo trong tệp nguồn thực của bạn.

+1

Ý tưởng hay, đặc biệt vì nó nhận được tất cả các cuộc gọi API không được chấp nhận ở một nơi. Tôi đã chuyển mã tương thích vào một tệp riêng biệt, thêm các phương thức '-compatibility *' vào các danh mục trên các lớp được đề cập (ví dụ: '- [NSFileManager compatibleRemoveItemAtPath:]'). – s4y

6

Tôi không chắc liệu clang có đủ thông minh để nắm bắt điều này hay không, nhưng nếu không, bạn có thể thử sử dụng performSelector:withObject:withObject: hoặc xây dựng và gọi một đối tượng NSInvocation.

+2

performSelector: và kin là giải pháp đúng để gọi các phương thức Objective-C khi chạy khi bạn không chắc chắn liệu chúng có tồn tại hay không. – cdespinosa

5

Bạn chỉ có thể đúc fileManager một id-ids có thể tham khảo bất kỳ đối tượng Objective-C, do đó trình biên dịch là không được phép kiểm tra các phương pháp được gọi là trên một:

[(id)fileManager removeItemAtPath:downloadDir error:NULL]; 

shouldn 't tăng bất kỳ cảnh báo hoặc lỗi nào.

Tất nhiên, điều này làm tăng các vấn đề khác - cụ thể là bạn mất tất cả kiểm tra thời gian biên dịch cho các phương thức được gọi là id. Vì vậy, nếu bạn viết sai chính tả tên phương thức, v.v., nó sẽ không bị bắt cho đến khi dòng mã được thực thi.

+1

Cùng một giải pháp nhưng một chút ít cho phép có thể được tìm thấy ở đây: http://vgable.com/blog/2009/06/15/ignoring-just-one-deprecated-warning/ – MonsieurDart

3

Nếu bạn xem xét bất kỳ hình thức "gây nhầm lẫn" trình biên dịch là một giải pháp không hợp lệ, có thể bạn sẽ phải sống với cảnh báo. (Trong cuốn sách của tôi, nếu bạn yêu cầu làm thế nào để loại bỏ một cảnh báo, nó không khôn ngoan để nhìn một con ngựa món quà trong miệng và nói điều gì đó không hợp lệ chỉ vì nó không giống như bạn mong đợi.)

câu trả lời mà làm việc tại thời gian chạy liên quan đến mặt nạ hoạt động đó đang xảy ra với công văn năng động, do đó trình biên dịch không phàn nàn về các cuộc gọi không được chấp nhận. Nếu bạn không thích phương pháp đó, bạn có thể tắt "Cảnh báo về các chức năng không được chấp nhận" trong dự án Xcode hoặc cài đặt đích của bạn, nhưng đó thường là một ý tưởng tồi. Bạn muốn biết về các API không dùng nữa, nhưng trong trường hợp này bạn muốn sử dụng nó mà không cần cảnh báo. Có những cách dễ dàng và khó khăn để làm điều này, và tỷ lệ cược là bạn sẽ xem xét tất cả chúng "không hợp lệ" trong một số hình thức, nhưng điều đó không ngăn cản chúng được hiệu quả, thậm chí chính xác.;-)

Một cách có thể để tránh những lời cảnh báo nhưng vẫn chọn trong thời gian chạy là sử dụng objc_msgSend() trực tiếp:

objc_msgSend(fileManager, @selector(removeFileAtPath:error:), downloadDir, nil]; 

This is what the Objective-C runtime does under the covers anyway, và cần đạt được kết quả bạn muốn với tối thiểu là phiền phức. Bạn thậm chí có thể rời khỏi dòng ban đầu đã nhận xét ở trên nó cho rõ ràng. Tôi biết tài liệu nói rằng, "Trình biên dịch tạo ra các cuộc gọi đến chức năng nhắn tin. Bạn không bao giờ nên gọi nó trực tiếp trong mã bạn viết." Một mình bạn phải quyết định khi nào thì không thể bẻ cong các quy tắc.

+2

Mã nên nói những gì nó có nghĩa là. Nếu không, thì API, ngôn ngữ hoặc lập trình viên bị hỏng. Nếu trình biên dịch cảnh báo khi lập trình viên tự tin rằng mã của mình là chính xác, thì trình biên dịch cần tìm hiểu về tình huống đó và bỏ qua nó một cách tự động hoặc cần hỗ trợ cú pháp cho phép bỏ qua cảnh báo cụ thể trên dòng đó (giống như cú pháp tôi đã đăng không hoạt động trong Clang đi kèm với Xcode). Bất cứ điều gì khác là một hack. – s4y

+2

Tôi hoan nghênh chủ nghĩa duy tâm của bạn, nhưng đôi khi bạn phải thỏa hiệp. Mã tôi đã đăng và những người khác cũng có, * không * nói điều đó có nghĩa là gì. Tôi tin rằng bạn có nghĩa là trình biên dịch sẽ có thể thần thánh ý định của bạn dựa hoàn toàn vào mã, mà không nhảy qua hoops. Đó là tốt đẹp và tất cả, nhưng làm thế nào để bạn đề xuất trình biên dịch nên biết rằng bạn sẽ chỉ gọi một phương pháp nhất định về thời gian chạy, nơi nó không được chấp nhận? Không có thông tin ngữ nghĩa bổ sung, nó thực sự không thể. Cú pháp có thể truyền đạt ý nghĩa như vậy sẽ là tuyệt vời, nhưng cho đến khi nó tồn tại, gọi mọi thứ khác một hack là không hiệu quả. –

+1

Đôi khi viết mã không rõ ràng với người quan sát thông thường là điều không thể tránh khỏi. Đây là thời điểm hoàn hảo để thêm nhận xét làm rõ nếu cần. Sử dụng hoặc 'performSelector: withObject: withObject:' hoặc 'objc_msgSend()' là các phương pháp gián đoạn tối thiểu vẫn truyền đạt ý nghĩa của bạn và tránh cảnh báo. Java có chú thích để ngăn chặn cảnh báo, nhưng C thì không. Rất tiếc, nhưng cuộc sống vẫn tiếp diễn. Tôi không bảo vệ tình trạng hiện tại của mọi thứ như nó phải như thế nào, chỉ cho thấy rằng nếu bạn đang thực sự sử dụng ngôn ngữ, nó có hiệu quả hơn để nắm lấy quirks của nó hơn là elitist. –