2012-05-09 16 views
19

मैं सोच रहा था कि जब आप एक जीसीडी ब्लॉक में दोहराने वाले टाइमर बनाते हैं तो यह काम नहीं करता है?जीसीडी के साथ एनएसटीमर को दोहराएं?

यह ठीक काम करता है:

-(void)viewDidLoad{ 
    [super viewDidLoad]; 
    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(runTimer) userInfo:nil repeats:YES]; 
} 
-(void)runTimer{ 
    NSLog(@"hi"); 
} 

लेकिन यह doesent काम:

dispatch_queue_t myQueue; 

-(void)viewDidLoad{ 
    [super viewDidLoad]; 

    myQueue = dispatch_queue_create("someDescription", NULL); 
    dispatch_async(myQueue, ^{ 
     [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(runTimer) userInfo:nil repeats:YES]; 
    }); 
} 
-(void)runTimer{ 
    NSLog(@"hi"); 
} 

उत्तर

57

NSTimers पर निर्धारित कर रहे हैं वर्तमान थ्रेड run loop। हालांकि, जीसीडी प्रेषण धागे में रन लूप नहीं होते हैं, इसलिए जीसीडी ब्लॉक में शेड्यूलिंग टाइमर कुछ भी नहीं करने जा रहे हैं।

वहाँ तीन उचित विकल्प है:

  1. चित्रा बाहर क्या पाश चलाने ऐसा आप पर टाइमर शेड्यूल करना चाहते हैं, और स्पष्ट रूप से। टाइमर बनाने के लिए +[NSTimer timerWithTimeInterval:target:selector:userInfo:repeats:] का उपयोग करें और फिर -[NSRunLoop addTimer:forMode:] वास्तव में उस रन लूप पर शेड्यूल करें जिसका आप उपयोग करना चाहते हैं। इसके लिए रन लूप पर एक हैंडल रखने की आवश्यकता है, लेकिन यदि आप इसे मुख्य थ्रेड पर करना चाहते हैं तो आप +[NSRunLoop mainRunLoop] का उपयोग कर सकते हैं।
  2. टाइमर-आधारित dispatch source का उपयोग करने के लिए स्विच करें। यह एक जीसीडी-जागरूक तंत्र में एक टाइमर लागू करता है, जो आपकी पसंद की कतार पर इच्छित अंतराल पर एक ब्लॉक चलाएगा। टाइमर बनाने से पहले
  3. स्पष्टीकरण dispatch_async() मुख्य कतार में वापस। यह मुख्य रन लूप का उपयोग करके विकल्प # 1 के बराबर है (क्योंकि यह मुख्य धागे पर टाइमर भी बनायेगा)।

बेशक, असली सवाल यह है कि, आप एक जीसीडी कतार से टाइमर क्यों शुरू कर रहे हैं?

+0

केविन की 2 जवाब निम्नलिखित एक और विकल्प, मैं MSWeakTimer साथ NSTimer (github.com/mindsnacks/MSWeakTimer) की जगह मेरी मुद्दों को हल किया, और सिर्फ dispatch_get_global_queue पारित कर दिया (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) प्रेषण के रूप में। –

+0

ग्रेट सारांश। सुपर धन्यवाद। अगर मैं सही ढंग से समझता हूं: 1. क्या आप कह रहे हैं कि कोई भी जीसीडी प्रेषण धागा मुख्य थ्रेड के लिए ** को छोड़कर एक रन लूप ** है? 2. यदि सही है तो प्रेषण के बारे में कैसे? यह रनलॉप के बिना कैसे प्रक्रिया करेगा? या यह है कि यह एक जीसीडी जागरूक तंत्र में भी लागू किया गया है? – Honey

+0

@ हनी डिस्पैच थ्रेड में रन लूप नहीं हैं। मुख्य धागा एक प्रेषण धागा नहीं है (ठीक है, जब तक कि आप 'dispatch_main() 'का उपयोग नहीं कर रहे हैं, और यदि ऐसा है, तो इसमें कोई रनलॉप नहीं है, लेकिन आप संभवतः ऐसा नहीं कर रहे हैं)। मुख्य धागा का रनलोप रनयूप के सामान्य ऑपरेशन के हिस्से के रूप में मुख्य कतार को स्पष्ट रूप से निकालने के लिए जीसीडी के साथ एकीकृत करता है। 'dispatch_after' जीसीडी का हिस्सा है (इसलिए नाम में" प्रेषण "शब्द), इसमें रनलोप्स के साथ कुछ भी नहीं है। –

-1

यह खराब आइडिया है। मैं इस जवाब को हटाने वाला था, लेकिन मैंने इसे एक ही गलती करने से रोकने के लिए यहां छोड़ा था। इस पर इशारा करने के लिए # केविन_लार्ड धन्यवाद। अपने कतार के बाद से

-(void)viewDidLoad{ 
     [super viewDidLoad]; 

     myQueue = dispatch_queue_create("someDescription", NULL); 
     dispatch_async(myQueue, ^{ 
      [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(runTimer) userInfo:nil repeats:YES]; 
      [[NSRunLoop currentRunLoop] run] 
     }); 
    } 

:

आप केवल अपने उदाहरण के लिए एक पंक्ति जोड़ना होगा और यह काम करता हूँ बस के रूप में आप इसे लिखा है:

[[NSRunLoop currentRunLoop] run]

ताकि आप चाहते हैं myQueue में एक एनएसटीएचड है और इसमें एनएसआरनलूप शामिल है और चूंकि dispatch_async में कोड उस एनएसटीएचएड का संदर्भ चलाता है, currentRunLoop आपकी कतार के धागे से जुड़े एक बंद रन लूप को वापस कर देगा।

+1

यह एक बुरा विचार है। अब आपने रनलॉप के रूप में उपयोग करने के लिए एक प्रेषण धागा को अपनाना है, और आप कभी भी थ्रेड के नियंत्रण को जीसीडी पर वापस नहीं लौट रहे हैं। यह मूल रूप से एक प्रेषण ब्लॉक में एक अनंत लूप डालने की तरह है। प्रेषण धागे पर लेना और कभी भी जीसीडी में उनका नियंत्रण वापस नहीं करना पूरे सिस्टम के प्रदर्शन को नुकसान पहुंचाएगा, और यदि आप पर्याप्त धागे के लिए ऐसा करते हैं तो आप वास्तव में गैर-वैश्विक कतारों में जमा जीसीडी स्टॉप प्रसंस्करण ब्लॉक बना सकते हैं। –

+0

आपको कोड को रनलोप से बाहर निकलने का तरीका चाहिए। एनएसटीमर का संदर्भ रखें और उचित समय में इसे अमान्य करें। – seedante

0

एनएसटीमर को थ्रेड के रनलोप के लिए निर्धारित किया गया है। प्रश्न के संहिता में, जीसीडी द्वारा प्रेषित धागे का रनलोप नहीं चल रहा है। आपको इसे मैन्युअल रूप से प्रारंभ करना होगा और रन लूप से बाहर निकलने का एक तरीका होना चाहिए, इसलिए आपको एनएसटीमर का संदर्भ रखना चाहिए, और उचित समय में इसे अमान्य करना चाहिए।

एनएसटीमर के पास लक्ष्य के लिए मजबूत संदर्भ है, इसलिए लक्ष्य टाइमर के लिए मजबूत संदर्भ नहीं हो सकता है, और रनलोप टाइमर के लिए मजबूत संदर्भ है।

weak var weakTimer: Timer? 
func configurateTimerInBackgroundThread(){ 
    DispatchQueue.global().async { 
     // Pause program execution in Xcode, you will find thread with this name 
     Thread.current.name = "BackgroundThreadWithTimer" 
     // This timer is scheduled to current run loop 
     self.weakTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(runTimer), userInfo: nil, repeats: true) 
     // Start current runloop manually, otherwise NSTimer won't fire. 
     RunLoop.current.run(mode: .defaultRunLoopMode, before: Date.distantFuture) 
    } 
} 

@objc func runTimer(){ 
    NSLog("Timer is running in mainThread: \(Thread.isMainThread)") 
} 

यदि भविष्य में टाइमर अमान्य हो गया है, तो एक्सकोड में फिर से प्रोग्राम निष्पादन रोकें, आपको पता चलेगा कि थ्रेड चला गया है।

बेशक, जीसीडी द्वारा भेजे गए धागे में रनलोप है। जीसीडी आंतरिक रूप से थ्रेड उत्पन्न करता है और पुन: उपयोग करता है, वहां थ्रेडर कॉलर के लिए अज्ञात होते हैं। यदि आप इसे सुरक्षित महसूस नहीं करते हैं, तो आप थ्रेड का उपयोग कर सकते हैं। डरो मत, कोड बहुत आसान है।

दरअसल, मैं पिछले हफ्ते एक ही चीज़ की कोशिश करता हूं और पूछताछ के साथ असफल हो जाता हूं, फिर मुझे यह पृष्ठ मिला। मैं हारने से पहले एनएसटीएचड की कोशिश करता हूं। यह काम करता हैं। तो जीसीडी में एनएसटीमर क्यों काम नहीं कर सकता है? यह होना चाहिए। NSTimer कैसे काम करता है यह जानने के लिए runloop's document पढ़ें।

NSTimer के साथ काम करने का प्रयोग करें NSThread:

func configurateTimerInBackgroundThread(){ 
    let thread = Thread.init(target: self, selector: #selector(addTimerInBackground), object: nil) 
    thread.name = "BackgroundThreadWithTimer" 
    thread.start() 
} 

@objc func addTimerInBackground() { 
    self.weakTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(runTimer), userInfo: nil, repeats: true) 
    RunLoop.current.run(mode: .defaultRunLoopMode, before: Date.distantFuture) 
} 

 संबंधित मुद्दे

  • कोई संबंधित समस्या नहीं^_^