Câu trả lời:
Về cơ bản PerformSelector cho phép bạn xác định động bộ chọn nào sẽ gọi bộ chọn trên đối tượng nhất định. Nói cách khác, bộ chọn không cần được xác định trước thời gian chạy.
Vì vậy, mặc dù chúng tương đương:
[anObject aMethod];
[anObject performSelector:@selector(aMethod)];
Hình thức thứ hai cho phép bạn làm điều này:
SEL aSelector = findTheAppropriateSelectorForTheCurrentSituation();
[anObject performSelector: aSelector];
trước khi bạn gửi tin nhắn.
performSelector:
là điều bạn có thể chỉ làm nếu bạn triển khai target-action trong lớp của mình. Các anh chị em performSelectorInBackground:withObject:
và performSelectorOnMainThread:withObject:waitUntilDone:
thường hữu ích hơn. Để tạo chuỗi nền và gọi lại kết quả cho chuỗi chính từ chuỗi nền đã nói.
performSelector
cũng hữu ích để ngăn chặn các cảnh báo biên dịch. Nếu bạn biết phương thức tồn tại (như sau khi sử dụng respondsToSelector
), nó sẽ ngăn Xcode nói "có thể không phản hồi your_selector
". Chỉ cần không sử dụng nó thay vì tìm ra nguyên nhân thực sự của cảnh báo. ;)
Đối với ví dụ rất cơ bản này trong câu hỏi,
[object doSomething];
[object performSelector:@selector(doSomething)];
không có sự khác biệt về những gì sắp xảy ra. doSomething sẽ được thực thi đồng bộ bởi đối tượng. Chỉ có "doSomething" là một phương thức rất đơn giản, không trả về bất kỳ thứ gì và không yêu cầu bất kỳ tham số nào.
nó có phải là một cái gì đó phức tạp hơn một chút, như:
(void)doSomethingWithMyAge:(NSUInteger)age;
mọi thứ sẽ trở nên phức tạp, bởi vì [object doSomethingWithMyAge: 42];
không còn có thể được gọi với bất kỳ biến thể nào của "performanceSelector", bởi vì tất cả các biến thể có tham số chỉ chấp nhận tham số đối tượng.
Bộ chọn ở đây sẽ là "doSomethingWithMyAge:" nhưng bất kỳ nỗ lực nào để
[object performSelector:@selector(doSomethingWithMyAge:) withObject:42];
đơn giản là sẽ không biên dịch. chuyển một NSNumber: @ (42) thay vì 42, cũng không giúp ích được gì, bởi vì phương thức yêu cầu một kiểu C cơ bản - không phải một đối tượng.
Ngoài ra, còn có các biến thể performanceSelector lên đến 2 tham số, không hơn. Trong khi các phương thức nhiều lần có nhiều tham số hơn.
Tôi đã phát hiện ra rằng mặc dù các biến thể đồng bộ của PerformSelector:
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
luôn trả về một đối tượng, tôi cũng có thể trả về một BOOL hoặc NSUInteger đơn giản và nó đã hoạt động.
Một trong hai cách sử dụng chính của PerformSelector là soạn động tên của phương thức bạn muốn thực thi, như đã giải thích trong câu trả lời trước. Ví dụ
SEL method = NSSelectorFromString([NSString stringWithFormat:@"doSomethingWithMy%@:", @"Age");
[object performSelector:method];
Cách sử dụng khác là gửi một thông điệp đến đối tượng một cách không đồng bộ, thông báo đó sẽ được thực thi sau trên runloop hiện tại. Đối với điều này, có một số biến thể PerformSelector khác.
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;
- (void)performSelector:(SEL)aSelector target:(id)target argument:(id)arg order:(NSUInteger)order modes:(NSArray *)modes;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg;
(vâng, tôi đã tập hợp chúng từ một số danh mục lớp Foundation, như NSThread, NSRunLoop và NSObject)
Mỗi biến thể có hành vi đặc biệt của riêng nó, nhưng tất cả đều có điểm chung (ít nhất là khi waitUntilDone được đặt thành KHÔNG). Lệnh gọi "performanceSelector" sẽ trả về ngay lập tức và thông báo tới đối tượng sẽ chỉ được đưa vào runloop hiện tại sau một thời gian.
Do quá trình thực thi bị trì hoãn - tự nhiên không có giá trị trả về nào tạo nên phương thức của bộ chọn, do đó giá trị trả về - (void) trong tất cả các biến thể không đồng bộ này.
Tôi hy vọng tôi đã che điều này bằng cách nào đó ...
@ennuikiller là đúng. Về cơ bản, các bộ chọn được tạo động sẽ hữu ích khi bạn không (và thường là không thể) biết tên của phương thức bạn sẽ gọi khi biên dịch mã.
Một điểm khác biệt chính là -performSelector:
và bạn bè (bao gồm cả biến thể đa luồng và biến thể trễ ) có phần hạn chế ở chỗ chúng được thiết kế để sử dụng với các phương pháp có tham số 0-2. Ví dụ: gọi -outlineView:toolTipForCell:rect:tableColumn:item:mouseLocation:
với 6 tham số và trả về NSString
là khá khó sử dụng và không được hỗ trợ bởi các phương thức được cung cấp.
NSInvocation
đối tượng.
performSelector:
và bạn bè đều lấy đối số đối tượng, nghĩa là bạn không thể sử dụng chúng để gọi (ví dụ) setAlphaValue:
, vì đối số của nó là một float.
Bộ chọn hơi giống con trỏ hàm trong các ngôn ngữ khác. Bạn sử dụng chúng khi bạn không biết lúc biên dịch phương thức nào bạn muốn gọi trong thời gian chạy. Ngoài ra, giống như con trỏ hàm, chúng chỉ gói gọn phần động từ của lời gọi. Nếu phương thức có các tham số, bạn cũng cần phải chuyển chúng.
An NSInvocation
phục vụ một mục đích tương tự, ngoại trừ việc nó liên kết với nhiều thông tin hơn. Nó không chỉ bao gồm phần động từ, nó còn bao gồm tân ngữ và các tham số. Điều này rất hữu ích khi bạn muốn gọi một phương thức trên một đối tượng cụ thể với các tham số cụ thể, không phải bây giờ mà trong tương lai. Bạn có thể xây dựng một thích hợp NSInvocation
và khai hỏa nó sau.
Có một sự khác biệt nhỏ giữa hai điều này.
[object doSomething]; // is executed right away
[object performSelector:@selector(doSomething)]; // gets executed at the next runloop
Đây là đoạn trích từ Tài liệu Apple
"performanceSelector: withObject: afterDelay: Thực hiện bộ chọn được chỉ định trên luồng hiện tại trong chu kỳ vòng lặp chạy tiếp theo và sau một khoảng thời gian trễ tùy chọn. Bởi vì nó đợi cho đến chu kỳ vòng lặp chạy tiếp theo để thực hiện bộ chọn, các phương pháp này cung cấp độ trễ nhỏ tự động từ mã hiện đang thực thi. Nhiều bộ chọn trong hàng đợi được thực hiện lần lượt theo thứ tự chúng đã được xếp hàng. "
performSelector:withObject:afterDelay:
, nhưng câu hỏi và đoạn mã của bạn đang sử dụng performSelector:
, là một phương pháp hoàn toàn khác. Từ tài liệu dành cho nó: <quote> performSelector:
Phương pháp này tương đương với việc gửi aSelector
tin nhắn trực tiếp đến người nhận. </quote>
performSelector/performSelector:withObject/performSelector:withObject:afterDelay
tất cả đều hành xử theo cùng một cách, đó là một sai lầm.