Làm thế nào để thoát khỏi cảnh báo 'bộ chọn không khai báo'


162

Tôi muốn sử dụng một bộ chọn trên một cá thể NSObject mà không cần một giao thức được triển khai. Ví dụ: có một phương thức thể loại nên đặt thuộc tính lỗi nếu đối tượng NSObject được gọi là hỗ trợ nó. Đây là mã và mã hoạt động như dự định:

if ([self respondsToSelector:@selector(setError:)])
{
    [self performSelector:@selector(setError:) withObject:[NSError errorWithDomain:@"SomeDomain" code:1 userInfo:nil]];
}

Tuy nhiên, trình biên dịch không thấy bất kỳ phương thức nào xung quanh với chữ ký setError: vì vậy nó đưa ra một cảnh báo cho mỗi dòng có chứa @selector(setError:)đoạn mã:

Undeclared selector 'setError:'

Tôi không muốn phải khai báo một giao thức để loại bỏ cảnh báo này, vì tôi không muốn tất cả các lớp có thể sử dụng giao thức này để thực hiện bất cứ điều gì đặc biệt. Chỉ bằng quy ước tôi muốn họ có một setError:phương pháp hoặc tài sản.

Đây có phải là có thể làm được? Làm sao?

Chúc mừng,
EP



Một bộ chọn không dùng nữa sẽ gây ra cảnh báo. Không còn an toàn để truy cập vào bộ chọn nữa bởi vì bộ chọn có thể bị xóa vào một lúc nào đó.
DawnSong

Câu trả lời:


254

Một tùy chọn khác là vô hiệu hóa cảnh báo với:

#pragma GCC diagnostic ignored "-Wundeclared-selector"

Bạn có thể đặt dòng này trong tệp .m nơi cảnh báo xảy ra.

Cập nhật:

Nó cũng hoạt động với LLVM như thế này:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"

... your code here ...

#pragma clang diagnostic pop

#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundeclared-selector" // Do your thing #pragma clang diagnostic pop
chóng mặt

vâng, nó làm như @dizy bang. (Xin lỗi vì trả lời muộn, nhưng tôi đã bỏ lỡ thông báo).
Klaas

Tôi rất cần#pragma clang diagnostic ignored "-Wselector"
tối đa

1
@mdorseif Hầu hết thời gian cảnh báo mà bạn 'phải' loại trừ được liệt kê trong nhật ký biên dịch. Bạn có thể tắt tiếng bất kỳ cảnh báo với khái niệm này. Vui mừng bạn đã thêm của bạn liên quan đến bộ chọn.
Klaas

@epologee bạn có thể thực hiện tương tự thông qua cài đặt bản dựng "Bộ chọn không khai báo"

194

Hãy xem NSSelectorFromString .

 SEL selector = NSSelectorFromString(@"setError:");
 if ([self respondsToSelector:selector])

Nó sẽ cho phép bạn tạo một bộ chọn trong thời gian chạy, thay vì vào thời gian biên dịch thông qua @selectortừ khóa và trình biên dịch sẽ không có cơ hội để phàn nàn.


Xin chào @sergio, cả câu trả lời của bạn và @ jacobrelkin đều hoạt động. Khá nhiều nộp cùng một lúc. Bạn sẽ giúp tôi chọn câu trả lời 'tốt hơn', nếu có?
epologee

2
Tôi thích câu trả lời này hơn vì nó trông giống "Ca cao" -y (?). Các sel_registerName()hình thingy làm mờ và loại bạn không nên gọi trực tiếp trừ khi bạn biết những gì bạn đang làm, kinda như obj_msg_send ();)
Nicolas Miari

15
Không chắc đó có phải là Xcode 5 hay không, nhưng tôi nhận được một cảnh báo khác với cách triển khai này: "PerformanceSelector có thể gây rò rỉ vì không biết bộ chọn của nó" .
Hampden123

1
@ Hampden123: đó là một vấn đề khác. có một cái nhìn ở đây: stackoverflow.com/questions/7017281/ từ
sergio

52

Tôi nghĩ điều này là do một số lý do kỳ lạ, bộ chọn không được đăng ký với thời gian chạy.

Hãy thử đăng ký bộ chọn qua sel_registerName():

SEL setErrorSelector = sel_registerName("setError:");

if([self respondsToSelector:setErrorSelector]) {
   [self performSelector:setErrorSelector withObject:[NSError errorWithDomain:@"SomeDomain" code:1 userInfo:nil]];
}

Xin chào @jacobrelkin, cả câu trả lời của bạn và @ sergio đều hoạt động. Khá nhiều nộp cùng một lúc. Bạn sẽ giúp tôi chọn câu trả lời 'tốt hơn', nếu có?
epologee

2
@epologee NSSelectorFromStringgọi sel_registerName()dưới mui xe nào. Chọn cái nào phù hợp với bạn hơn.
Jacob Relkin

1
@epologee Tôi nghĩ rằng gọi sel_registerName()trực tiếp rõ ràng hơn về lý do tại sao bạn làm việc đó. NSSelectorFromStringkhông nói với bạn rằng nó sẽ cố gắng đăng ký bộ chọn.
Jacob Relkin

8
Không chắc đó có phải là Xcode 5 hay không, nhưng tôi nhận được một cảnh báo khác với cách triển khai này: "PerformanceSelector có thể gây rò rỉ vì không biết bộ chọn của nó" .
Hampden123

@ Max_Power89 Không. Xem các bình luận khác của tôi bên dưới. Tôi không muốn dành quá nhiều thời gian cho việc này vì vậy tôi chỉ đơn giản bao gồm các tệp tiêu đề.
Hampden123

7

Tôi đã nhận được thông báo đó bằng cách # bao gồm các tệp với phương thức. Không có gì khác được sử dụng từ tập tin đó.


Mặc dù đây là giải pháp ít duyên dáng hơn, nhưng nó hoạt động với tôi vì tôi có "nghi phạm đã biết", người có thể đang nhận bộ chọn. Ngoài ra, nếu tôi triển khai cách tiếp cận bộ chọn thời gian chạy, tôi vẫn nhận được một cảnh báo khác tại câu lệnh PerformanceSelector; cụ thể là, "PerformanceSelector có thể gây rò rỉ vì không biết bộ chọn của nó" . Vì vậy, cảm ơn!
Hampden123

2
Cả hai câu trả lời được bình chọn hàng đầu đều đúng. Mục đích của cảnh báo "bộ chọn không khai báo" là bắt lỗi tại thời điểm biên dịch nếu bạn thay đổi tên của bộ chọn mà bạn đang dựa vào. Vì vậy, chính xác nhất là #import tệp khai báo phương thức bạn đang dựa vào.
Brane

7

Tôi nhận ra rằng tôi hơi muộn với chủ đề này nhưng để hoàn thiện, bạn có thể tắt cảnh báo này trên toàn cầu bằng cách sử dụng cài đặt xây dựng mục tiêu.

Trong phần, 'Apple LLVM cảnh báo - Objective-C', thay đổi:

Undeclared Selector - NO

6

Nếu lớp của bạn triển khai phương thức setError: (thậm chí bằng cách khai báo động bộ setter của thuộc tính lỗi cuối cùng), bạn có thể muốn khai báo nó trong tệp giao diện của mình (.h) hoặc nếu bạn không muốn hiển thị theo cách đó bạn có thể thử với thủ thuật lừa đảo PrivateMethods:

@interface Yourclass (PrivateMethods)

- (void) yourMethod1;
- (void) yourMethod2;

@end

ngay trước khi @im THỰC HÀNH của bạn, điều này sẽ ẩn các cảnh báo;).


Cảm ơn, nhưng tôi đang gọi phương thức từ một danh mục, vì vậy điều này không áp dụng. Chúc mừng, EP.
epologee

Và một số người trong chúng ta đang làm những điều kỳ lạ hơn - bộ chọn được triển khai trong một đối tượng F #, trong trường hợp của tôi.
James Moore

1
Điều này không loại bỏ cảnh báo trong XCode 7.1.1 / iOS 9.1, tôi có thể thấyPerformSelector may cause a leak because its selector is unknown
loretoparisi

3

Một vĩ mô thực sự cảm thấy thoải mái để đưa vào của bạn .pchhoặc Common.hhoặc bất cứ nơi nào bạn muốn:

#define SUPPRESS_UNDECLARED_SELECTOR_LEAK_WARNING(code)                        \
_Pragma("clang diagnostic push")                                        \
_Pragma("clang diagnostic ignored \"-Wundeclared-selector"\"")     \
code;                                                                   \
_Pragma("clang diagnostic pop")                                         \

Đây là bản chỉnh sửa của câu hỏi này cho vấn đề tương tự ...


3

Bạn có thể tắt nó trong Xcode như trong ảnh chụp màn hình:

nhập mô tả hình ảnh ở đây


Đẹp một. Tuy nhiên, tôi chỉ thích vô hiệu hóa cảnh báo cho các trường hợp rõ ràng, bằng cách nói "clang là sai trong dịp này, tôi biết tôi đang làm gì". Cảm ơn vì đầu vào của bạn!
epologee

2

Trước tiên, bạn cũng có thể truyền đối tượng được đề cập đến id để tránh cảnh báo:

if ([object respondsToSelector:@selector(myMethod)]) {
    [(id)object myMethod];
}

1
Điều này không loại bỏ cảnh báo tương tự về nội dung biểu thức if, cho đến tận XC7.1 cho đến ngày nay.
Martin-Gilles Lavoie

2

Một cách khác để tránh cảnh báo này là đảm bảo phương thức chọn của bạn trông như thế này:

-(void) myMethod :(id) sender{
}

Đừng quên "(id) người gửi" nếu bạn muốn chấp nhận bất kỳ người gửi nào hoặc chỉ định một loại đối tượng người gửi nếu bạn thích.


0

Mặc dù câu trả lời đúng có thể nằm ở việc thông báo cho Xcode thông qua việc nhập hoặc đăng ký bộ chọn rằng bộ chọn đó tồn tại, nhưng trong trường hợp của tôi, tôi đã thiếu dấu chấm phẩy. Hãy chắc chắn trước khi bạn "sửa" lỗi mà có lẽ, lỗi là chính xác và mã của bạn thì không. Tôi đã tìm thấy lỗi trong mẫu MVCNetworking của Apple chẳng hạn.


Không, câu trả lời đúng không phải là thông báo cho Xcode thông qua nhập khẩu, bởi vì những lần nhập đó đã được thực hiện. Câu trả lời đúng là câu trả lời ở trên được đánh dấu là ... câu trả lời đúng, mặc dù câu trả lời của @ sergio cũng sẽ giải quyết được vấn đề. Sử dụng bộ chọn sai không phải là chủ đề của câu hỏi này, do đó thay đổi bộ chọn không phải là một câu trả lời. Tôi sẽ tiết kiệm cho bạn downvote mặc dù.
epologee

1
Cảm ơn đã nhắc nhở tôi rằng tôi có lẽ nên sử dụng một nhận xét. Tất cả những gì tôi có thể nói là việc nhập thiếu cũng gây ra cảnh báo Xcode này, nếu không phải là trường hợp cụ thể này. Tôi chỉ đề xuất NSSelectorFromString hoặc các tùy chọn "đăng ký" khác khi xây dựng bộ chọn trong thời gian chạy hoặc trả lời các cuộc gọi phương thức theo kiểu động (ví dụ: methodSignatureForSelector). Đăng ký nó có nghĩa là bạn đang "xử lý lỗi" và do đó không đúng trong một số trường hợp, bởi vì cách tiếp cận đúng hơn sẽ là sửa lỗi cảnh báo (nếu phân tích tiếng kêu là chính xác.)
Louis St-Amour

Trên thực tế, bây giờ tôi thấy rằng câu hỏi ban đầu nói rõ ràng, "không cần giao thức được triển khai" - và hoàn toàn không đề cập đến nhập khẩu. Vì vậy, tôi sẽ đưa ra rằng nhập chính danh mục có thể là lựa chọn tốt nhất cho người dùng này. Bất cứ điều gì khác ở đây có thể định nghĩa bộ chọn hai lần, nói về mặt kỹ thuật. Đúng? - Chỉnh sửa: Ah, tôi đã thực hiện điều này quá xa. Cảm ơn phản hồi của bạn, tôi sẽ dừng ngay bây giờ. :)
Louis St-Amour

-1

Tôi đã có thể khiến cảnh báo biến mất bằng cách thêm phương thức xoa dịu (tiết lộ: Tôi không nghĩ về điều này nhưng đã tìm thấy nó bằng cách googling trên lịch trình theo lịch trình)

    [NSTimer scheduledTimerWithTimeInterval:[[NSDate distantFuture] timeIntervalSinceNow]
                                     target:self
                                   selector:@selector(donothingatall:)
                                   userInfo:nil
                                    repeats:YES];


    [[NSRunLoop currentRunLoop] run];

    HTTPLogVerbose(@"%@: BonjourThread: Aborted", THIS_FILE);

    }
}

+ (void) donothingatall:(NSTimer *)timer
{

}

Mặc dù tôi đánh giá cao việc biết cách che giấu cảnh báo, nhưng việc khắc phục nó sẽ tốt hơn và cả các kỹ thuật của Sergio và Relkin đều không hiệu quả với tôi, không rõ lý do.


1
Nếu người khác đọc giải pháp này, sẽ có hiệu quả , anh ấy / cô ấy sẽ khá bối rối, bao gồm cả bản thân tương lai của bạn. Nếu bạn chắc chắn rằng bạn biết những gì bạn đang làm bằng cách gọi một bộ chọn không tồn tại, do đó gây ra cảnh báo, hãy bỏ qua sơ khai phương thức gây hiểu lầm và đảm bảo mã của bạn thể hiện ý định của bạn.
epologee

1
Điểm tốt. Tôi đã làm việc với mã được kế thừa và chỉ cố gắng tìm ra cách làm cho cảnh báo biến mất, không cố gắng giải quyết câu hỏi cơ bản về lý do tại sao có một bộ chọn không tồn tại. Một bước tại một thời điểm, tôi luôn luôn nói.
dùng938797
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.