परफ़ेक्टर का उपयोग कैसे करें: withObject: afterDelay: कोको में आदिम प्रकार के साथ?


97

NSObjectविधि performSelector:withObject:afterDelay:मुझे एक निश्चित समय के बाद एक वस्तु तर्क के साथ वस्तु पर एक विधि को लागू करने की अनुमति देता है। इसका उपयोग गैर-ऑब्जेक्ट तर्क (जैसे ints, फ़्लोट्स, स्ट्रक्चर्स, नॉन-ऑब्जेक्ट पॉइंटर्स, आदि) के तरीकों के लिए नहीं किया जा सकता है।

एक गैर-वस्तु तर्क के साथ एक विधि के साथ एक ही चीज़ को प्राप्त करने का सबसे सरल तरीका क्या है ? मुझे पता है कि नियमित रूप से performSelector:withObject:, समाधान का उपयोग करना है NSInvocation(जो वास्तव में जटिल है)। लेकिन मुझे नहीं पता कि "देरी" भाग को कैसे संभालना है।

धन्यवाद,


यह थोड़ा हैकिश है लेकिन मुझे तेज कोड लिखना उपयोगी लगता है। कुछ इस तरह से: id ऑब्जेक्ट = [array performSelector: @selector (objectAtIndex :) withObject: (__bridge_transfer) (void *) 1]; । यदि तर्क 0 है, तो आपको पुल कास्ट की भी आवश्यकता नहीं है क्योंकि 0 "विशेष" है और आईडी इसके अनुरूप है।
रामी अल ज़ुहुरी

जवाबों:


72

यहाँ है कि मैं NSInvocation का उपयोग करते हुए कुछ बदल सकता था, जिसे मैं कॉल कर सकता था:

SEL theSelector = NSSelectorFromString(@"setOrientation:animated:");
NSInvocation *anInvocation = [NSInvocation
            invocationWithMethodSignature:
            [MPMoviePlayerController instanceMethodSignatureForSelector:theSelector]];

[anInvocation setSelector:theSelector];
[anInvocation setTarget:theMovie];
UIInterfaceOrientation val = UIInterfaceOrientationPortrait;
BOOL anim = NO;
[anInvocation setArgument:&val atIndex:2];
[anInvocation setArgument:&anim atIndex:3];

[anInvocation performSelector:@selector(invoke) withObject:nil afterDelay:1];

सिर्फ क्यों नहीं [theMovie setOrientation: UIInterfaceOrientationPortrait animated:NO]? या क्या आपका मतलब यह है कि invokeसंदेश आपके द्वारा निष्पादित करने के बाद की विधि में है?
पीटर होसी

आह हां, मैं यह कहना भूल गया .. `[anInvocation performSelector: @selector (invoke) afterDelay: 0.5];
Morty

24
जो लोग नहीं जानते हैं उनके लिए बस एक ध्यान दें, हम सूचकांक 2 से तर्क जोड़ना शुरू करते हैं क्योंकि सूचकांक 0 और 1 छिपे हुए तर्क "स्वयं" और "_cmd" के लिए आरक्षित हैं।
विशाल सिंह

35

बस NSNumber में फ्लोट, बूलियन, इंट या समान लपेटें।

संरचनाओं के लिए, मुझे एक आसान समाधान की जानकारी नहीं है, लेकिन आप एक अलग ObjC वर्ग बना सकते हैं जो इस तरह की संरचना का मालिक है।


5
एक आवरण विधि बनाएँ, -delayedMethodWithArgument: (NSNumber *) arg। NSNumber से आदिम को बाहर निकालने के बाद मूल विधि को कॉल करें।
जेम्स विलियम्स

1
समझ लिया। फिर मैं एक सुंदर शालीनता के बारे में निश्चित नहीं हूं, लेकिन आप एक और तरीका जोड़ सकते हैं, जो आपके प्रदर्शन के लक्ष्य के रूप में है: आपके द्वारा खोजा गया एक अन्य विचार NSObject पर एक श्रेणी बनाना है जो perforSelector जोड़ता है: withInt: ... (और इसी तरह)।
हानि पहुँचाता

32
NSValue (NSNumber का सुपरक्लास) आपके द्वारा दी गई किसी भी चीज़ को लपेट देगा। यदि आप 'बार' नामक 'स्ट्रक्चर फू' का एक उदाहरण लपेटना चाहते हैं, तो आप '[NSValue valueWithBytes: & bar objCType: @encode (स्ट्रक्चर फू)];'
जिम डोवे

2
आप NSValue का उपयोग किसी संरचना को लपेटने के लिए कर सकते हैं। किसी भी तरह से, NSNumber / NSValue सही समाधान है।
पीटर होसी

6
पुष्टि: चाहिए पारित nilएक के लिए BOOLपैरामीटर प्राप्त करने के लिए NO( FALSE)
निकोलस Miari

13

इस का उपयोग न करें। मैं केवल इतिहास के लिए इसे छोड़ दिया है। टिप्पणियाँ देखें।

BOOL पैरामीटर है, तो एक सरल चाल है।

हाँ के लिए NO और स्वयं के लिए nil पास करें। शून्य को NO के BOOL मान पर डाला जाता है। सेल्फ को YES के BOOL मूल्य पर रखा गया है।

यदि यह BOOL पैरामीटर के अलावा कुछ और है तो यह दृष्टिकोण टूट जाता है।

स्वयं को एक UIView मानता है।

//nil will be cast to NO when the selector is performed
[self performSelector:@selector(setHidden:) withObject:nil afterDelay:5.0];

//self will be cast to YES when the selector is performed
[self performSelector:@selector(setHidden:) withObject:self afterDelay:10.0];

मेरा मानना ​​है कि BOOL मान हमेशा 0 या 1 होना चाहिए। यह सच है कि अधिकांश कोड परवाह नहीं करेंगे और बस इस पर एक if ()परीक्षण करेंगे, लेकिन यह अनुमान योग्य है कि कुछ कोड 0 या 1 होने पर निर्भर करेगा, और इस तरह जीता '1 (YES) के समान होने पर कुछ बड़े एड्रेस वैल्यू को ट्रीट करें।
user102008

1
उसके लिए धन्यवाद, एक आवरण बनाना या ऐसा करने के लिए बदसूरत GCD सिंटैक्स लिखना नहीं चाहता था।
0xSina

10
यह किसी का उपयोग नहीं करते । यह दृष्टिकोण गंभीर कीड़े का स्रोत है, जिसे खोजना मुश्किल है। selfजैसे पते के साथ कल्पना करो 0x0123400। इस अनुमोदन के साथ, आपको 0.4% मामलों में हां का कोई इंस्टेंट नहीं मिलेगा । Worser, इस संभावना के साथ, यह समाधान सभी परीक्षणों को पारित कर सकता है और बाद में पता चलता है।
ओलेग ट्रेखमैन

1
UPD: मेमोरी संरेखण के कारण किसी को 6% प्रोबिलीली के साथ YES का NO इंस्टेंस मिलेगा
ओलेग ट्रेखमैन


8

शायद NSValue , बस यह सुनिश्चित करें कि आपके संकेत देरी के बाद भी मान्य हैं (यानी स्टैक पर कोई ऑब्जेक्ट आवंटित नहीं किया गया है)।


क्या कोई पुराना तरीका "अनबॉक्स" होगा NSValue(यदि संभव हो तो) अपेक्षित है type?
एलेक्स ग्रे

8

मुझे पता है कि यह एक पुराना सवाल है लेकिन अगर आप iOS SDK 4+ का निर्माण कर रहे हैं तो आप बहुत कम प्रयास के साथ इसे करने के लिए ब्लॉक का उपयोग कर सकते हैं और इसे अधिक पठनीय बना सकते हैं:

double delayInSeconds = 2.0;
int primitiveValue = 500;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    [self doSomethingWithPrimitive:primitiveValue];     
});

इससे रिटेन लूप बनता है। इसके लिए ठीक से काम करने के लिए आपको स्वयं के लिए एक कमजोर संदर्भ बनाने और चयनकर्ता को करने के लिए उस संदर्भ का उपयोग करने की आवश्यकता है। "__ब्लॉक टाइप (स्वयं) कमजोर स्वयं = स्वयं?"
टिम वाचर

1
यहां आपको एक कमजोर संदर्भ की आवश्यकता नहीं है क्योंकि स्वयं ब्लॉक को बनाए नहीं रखता है (कोई भी लूप बरकरार नहीं है)। यह वास्तव में एक मजबूत संदर्भ का उपयोग करने के लिए बेहतर है क्योंकि स्वयं अन्यथा डील हो सकती है। देखें: stackoverflow.com/a/19018633/251687
सेबस्टियन मार्टिन

6

PerformSelector: WithObject हमेशा एक ऑब्जेक्ट लेता है, इसलिए int / double / float आदि जैसे तर्क पास करने के लिए ..... आप इस तरह से कुछ का उपयोग कर सकते हैं।

//NSNumber is an object..

    [self performSelector:@selector(setUserAlphaNumber:) withObject: [NSNumber numberWithFloat: 1.0f]
    afterDelay:1.5];

    -(void) setUserAlphaNumber: (NSNumber*) number{

     [txtUsername setAlpha: [number floatValue] ];
    }

उसी तरह से आप [NSNumber numberWithInt:] आदि का उपयोग कर सकते हैं .... और प्राप्त करने की विधि में आप संख्या को अपने प्रारूप में [संख्या int] या [संख्या डबल] के रूप में परिवर्तित कर सकते हैं।


1
यदि कोई + performSelector: withObject: + तक ही सीमित है, तो यह एकमात्र सही उत्तर पोस्ट किया गया है, IMO। लेकिन @ MichaelGaylord का उपयोग ब्लॉक का जवाब क्लीनर है, अगर यह आपके लिए एक विकल्प है।
18_ 18

5

ब्लॉक जाने का रास्ता है। आपके पास जटिल पैरामीटर हो सकते हैं, सुरक्षा टाइप कर सकते हैं, और यह यहाँ के अधिकांश पुराने उत्तरों की तुलना में बहुत सरल और सुरक्षित है। उदाहरण के लिए, आप बस लिख सकते हैं:

[MONBlock performBlock:^{[obj setFrame:SOMETHING];} afterDelay:2];

ब्लॉक आपको मनमाने ढंग से पैरामीटर सूची, संदर्भ वस्तुओं और चर पर कब्जा करने की अनुमति देते हैं।

समर्थन कार्यान्वयन (मूल):

@interface MONBlock : NSObject

+ (void)performBlock:(void(^)())pBlock afterDelay:(NSTimeInterval)pDelay;

@end

@implementation MONBlock

+ (void)imp_performBlock:(void(^)())pBlock
{
 pBlock();
}

+ (void)performBlock:(void(^)())pBlock afterDelay:(NSTimeInterval)pDelay
{
  [self performSelector:@selector(imp_performBlock:)
             withObject:[pBlock copy]
             afterDelay:pDelay];
}

@end

उदाहरण:

int main(int argc, const char * argv[])
{
 @autoreleasepool {
  __block bool didPrint = false;
  int pi = 3; // close enough =p

  [MONBlock performBlock:^{NSLog(@"Hello, World! pi is %i", pi); didPrint = true;} afterDelay:2];

  while (!didPrint) {
   [NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeInterval:0.1 sinceDate:NSDate.date]];
  }

  NSLog(@"(Bye, World!)");
 }
 return 0;
}

एक अन्य उदाहरण के लिए माइकल का जवाब (+1) भी देखें।


3

मैं हमेशा इस बात का पुनर्मुद्रण करूंगा कि आप NSMutableArray का उपयोग उस वस्तु के रूप में करें जिस पर से गुजरना है। ऐसा इसलिए है क्योंकि आप कई ऑब्जेक्ट्स को पास कर सकते हैं, जैसे बटन दबाया और अन्य मान। NSNumber, NSInteger और NSString कुछ मूल्य के कंटेनर हैं। सुनिश्चित करें कि जब आप उस सरणी से ऑब्जेक्ट प्राप्त करें जिसे आप सही कंटेनर प्रकार के लिए संदर्भित करते हैं। आपको एनएस कंटेनरों पर पास होने की आवश्यकता है। वहां आप मूल्य का परीक्षण कर सकते हैं। याद रखें कि जब मूल्यों की तुलना की जाती है तो कंटेनर का उपयोग होता है।

#define DELAY_TIME 5

-(void)changePlayerGameOnes:(UIButton*)sender{
    NSNumber *nextPlayer = [NSNumber numberWithInt:[gdata.currentPlayer intValue]+1 ];
    NSMutableArray *array = [[NSMutableArray alloc]initWithObjects:sender, nil];
    [array addObject:nextPlayer];
    [self performSelector:@selector(next:) withObject:array afterDelay:DELAY_TIME];
}
-(void)next:(NSMutableArray*)nextPlayer{
    if(gdata != nil){ //if game choose next player
       [self nextPlayer:[nextPlayer objectAtIndex:1] button:[nextPlayer objectAtIndex:0]];
    }
}

2

मैं भी ऐसा करना चाहता था, लेकिन एक विधि के साथ जो BOOL पैरामीटर प्राप्त करता है। NSNumber के साथ बूल वैल्यू लपेटकर, मूल्य का भुगतान किया गया। मुझे कोई जानकारी नहीं है की क्यों।

तो मैं एक साधारण हैक कर रहा था। मैंने एक अन्य डमी फ़ंक्शन में आवश्यक पैरामीटर डाला और उस फ़ंक्शन को परफॉर्मेक्टर का उपयोग करके कॉल किया, जहां withObject = nil;

[self performSelector:@selector(dummyCaller:) withObject:nil afterDelay:5.0];

-(void)dummyCaller {

[self myFunction:YES];

}

हार्म्स के उत्तर पर मेरी टिप्पणी ऊपर देखें। ऐसा लगता है, क्योंकि -performSelector[...]विधि एक वस्तु (एक आदिम डेटा प्रकार के बजाय) की उम्मीद करती है, और यह नहीं जानती है कि बुलाया चयनकर्ता एक बूलियन को स्वीकार करता है, शायद NSNumberउदाहरण का पता (पॉइंटर वैल्यू) आँख बंद करके BOOL(=) है गैर-शून्य, यानी TRUE)। शायद रनटाइम इस से अधिक हो सकता है और एक शून्य बूलियन को एक में लपेटकर पहचान सकता है NSNumber!
निकोलस मिआरी

मैं सहमत हूँ। मुझे लगता है कि एक बेहतर बात यह है कि BOOL से पूरी तरह से बचें और NO और 1 के लिए पूर्णांक 0 का उपयोग करें, और इसे NSNumber या NSValue के साथ लपेटें।
जीनकोड जू

क्या वह कुछ बदलता है? यदि ऐसा है, तो अचानक मुझे कोको के साथ निराशा हुई है :)
निकोलस मारी

2

मुझे लगता है कि ऐसा करने का सबसे तेज (लेकिन कुछ हद तक गंदा) तरीका सीधे objc_msgSend को लागू करने से है। हालाँकि, इसे सीधे इनवॉइस करना खतरनाक है क्योंकि आपको दस्तावेज़ पढ़ने और यह सुनिश्चित करने की ज़रूरत है कि आप रिटर्न वैल्यू के प्रकार के लिए सही वेरिएंट का उपयोग कर रहे हैं और क्योंकि objc_msgSend को कंपाइलर सुविधा के लिए vararg के रूप में परिभाषित किया गया है, लेकिन वास्तव में फास्ट असेंबली गोंद के लिए लागू किया गया है । एक प्रतिनिधि विधि को कॉल करने के लिए उपयोग किए जाने वाले कुछ कोड यहां दिए गए हैं - [प्रतिनिधि पूर्णांकडीकचेंज:] जो एक पूर्णांक तर्क लेता है।

#import <objc/message.h>


SEL theSelector = @selector(integerDidChange:);
if ([self.delegate respondsToSelector:theSelector])
{
    typedef void (*IntegerDidChangeFuncPtrType)(id, SEL, NSInteger);
    IntegerDidChangeFuncPtrType MyFunction = (IntegerDidChangeFuncPtrType)objc_msgSend;
    MyFunction(self.delegate, theSelector, theIntegerThatChanged);
}

यह पहली बार चयनकर्ता को बचाता है क्योंकि हम इसे कई बार संदर्भित करने जा रहे हैं और टाइपो बनाना आसान होगा। यह तब पुष्टि करता है कि प्रतिनिधि वास्तव में चयनकर्ता को जवाब देता है - यह एक वैकल्पिक प्रोटोकॉल हो सकता है। यह तब एक फ़ंक्शन पॉइंटर प्रकार बनाता है जो चयनकर्ता के वास्तविक हस्ताक्षर को निर्दिष्ट करता है। ध्यान रखें कि सभी ऑब्जेक्टिव-सी मैसेज में दो छिपे हुए पहले तर्क होते हैं, ऑब्जेक्ट को मैसेज किया जा रहा है और चयनकर्ता को भेजा जा रहा है। फिर हम उपयुक्त प्रकार का एक फ़ंक्शन पॉइंटर बनाते हैं और इसे अंतर्निहित objc_msgSend फ़ंक्शन को इंगित करने के लिए सेट करते हैं। ध्यान रखें कि यदि रिटर्न मान एक फ्लोट या संरचना है, तो आपको objc_msgSend के एक अलग संस्करण का उपयोग करने की आवश्यकता है। अंत में, शीट के नीचे ऑब्जेक्टिव-सी का उपयोग करने वाले उसी मशीनरी का उपयोग करके संदेश भेजें।


क्या आपका मतलब है "कि आप तैरता या structs भेज रहा है ... कर रहे हैं ध्यान में रखें" लौटने तैरता या structs
user102008

वे तीन पंक्तियाँ आपको टाइप-सेफ्टी देती हैं और गारंटी देती हैं कि आपके चयनकर्ता से तर्कों की संख्या कितनी है। objc_msgSend एक वेरग फ़ंक्शन है जिसे वास्तव में असेंबली गोंद के रूप में लागू किया जाता है ताकि यदि आप टाइपकास्ट नहीं करते हैं, तो आप इसे कुछ भी दे सकते हैं।
जेसन हैरिस

1

आप बस चयनकर्ता को कॉल करने के लिए NSTimer का उपयोग कर सकते हैं:

[NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(yourMethod:) userInfo:nil repeats:NO]

आप तर्क याद कर रहे हैं। yourMethod:आपके उदाहरण में तर्क टाइमर ही है, और आपने इसके लिए कुछ भी पारित नहीं किया है userInfo। यहां तक ​​कि अगर आपने उपयोग किया था userInfo, तब भी आपको मूल्य को बॉक्स में रखना होगा, जैसा कि हर्म्स और रेमस रुसानु ने सुझाया था।
पीटर होसी

-1 प्रदर्शनकर्ता का उपयोग न करने और गैर-आवश्यक NSTimer उदाहरण बनाने के लिए
एप्लासा आईओएस डेवलपर

1

एक NSNumber या अन्य NSValue के साथ प्रदर्शनकर्ता को कॉल करने से काम नहीं चलेगा। NSValue / NSNumber के मूल्य का उपयोग करने के बजाय, यह प्रभावी रूप से सूचक को एक int, float, या जो कुछ भी उपयोग करता है और डालेगा।

लेकिन समाधान सरल और स्पष्ट है। NSInvocation बनाएं और कॉल करें

[invocation performSelector:@selector(invoke) withObject:nil afterDelay:delay]


0

पेप्स ... ठीक है, बहुत संभावना है, मैं कुछ याद कर रहा हूं, लेकिन क्यों न केवल एक ऑब्जेक्ट प्रकार बनाएं, जैसे कि NSNumber, आपके गैर-ऑब्जेक्ट प्रकार चर, जैसे CGFloat के लिए एक कंटेनर के रूप में कहें।

CGFloat myFloat = 2.0; 
NSNumber *myNumber = [NSNumber numberWithFloat:myFloat];

[self performSelector:@selector(MyCalculatorMethod:) withObject:myNumber afterDelay:5.0];

सवाल आदिम प्रकारों को पारित करने के बारे में है, न कि एनएसएनम्बर ऑब्जेक्ट्स के बारे में।
रुडोल्फ एडमकोविस

प्रश्न यह मानता है कि ओपी के पास केवल विधि तक बाहरी पहुंच है और हस्ताक्षर को बदल नहीं सकता है।
एलेक्स ग्रे
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.