iphone - चक्र बनाए रखने से बचने के लिए UIViewController के अंदर NSTimer को अमान्य करने का सर्वोत्तम समय




objective-c memory-management (6)

क्या किसी को पता है कि एक एनआईएसटीमर को रोकने का सबसे अच्छा समय कब होता है जो टाइमर और नियंत्रक के बीच चक्र बनाए रखने से बचने के लिए UIViewController के अंदर संदर्भ आयोजित किया जाता है?

यहां अधिक जानकारी में सवाल है: मेरे पास UIViewController के अंदर एक एनएसटीमर है।

दृश्य नियंत्रक के ViewDidLoad के दौरान, मैं टाइमर शुरू करता हूं:

statusTimer = [NSTimer scheduledTimerWithTimeInterval: 1 target: self selector: @selector(updateStatus) userInfo: nil repeats: YES];

उपरोक्त टाइमर को व्यू कंट्रोलर का संदर्भ रखने का कारण बनता है।

अब मैं अपना कंट्रोलर रिलीज करना चाहता हूं (पैरेंट कंट्रोलर इसे उदाहरण के लिए रिलीज़ करता है)

प्रश्न यह है कि टाइमर को नियंत्रक के संदर्भ को जारी करने के लिए मजबूर करने के लिए मैं [statusTimer अमान्य] कहां कॉल कर सकता हूं?

मैंने इसे ViewDidUnload में डालने का प्रयास किया, लेकिन जब तक दृश्य को स्मृति चेतावनी प्राप्त नहीं होती तब तक इसे निकाल दिया नहीं जाता है, इसलिए एक अच्छी जगह नहीं है। मैंने dealloc की कोशिश की, लेकिन जब तक टाइमर जीवित है (चिकन और अंडा समस्या) तक dealloc कभी नहीं कहा जाएगा।

कोई अच्छा सुझाव?


  1. आप के साथ शुरू करने के लिए बनाए रखने के चक्र से बच सकते हैं, उदाहरण के लिए, टाइमर को एक StatusUpdate ऑब्जेक्ट पर StatusUpdate करना जिसमें आपके नियंत्रक के लिए एक गैर-बनाए रखा (कमजोर) संदर्भ है, या एक StatusUpdater जो आपके नियंत्रक के सूचक के साथ प्रारंभ होता है, उस के लिए कमजोर संदर्भ, और आपके लिए टाइमर सेट करता है।

    • आप दृश्य को टाइमर को रोक सकते हैं -willMoveToWindow: जब लक्ष्य विंडो nil (जिसे counterexample को -viewDidDisappear: को -viewDidDisappear: चाहिए -viewDidDisappear: जो आपने प्रदान किया है) साथ ही इन -viewDidDisappear: :। इसका मतलब है कि आपका विचार आपके नियंत्रक में वापस आ रहा है; आप केवल नियंत्रक को एक- -view:willMoveToWindow: भेजकर टाइमर को पकड़ने से बचने से बच सकते हैं -view:willMoveToWindow: संदेश या अधिसूचना पोस्ट करके, यदि आप परवाह करते हैं।

    • संभवतः, आप खिड़की से दृश्य को हटाने का कारण बन रहे हैं, ताकि आप दृश्य को समझने वाली रेखा के साथ टाइमर को रोकने के लिए एक लाइन जोड़ सकें

    • आप एक गैर-दोहराव टाइमर का उपयोग कर सकते हैं। जैसे ही यह आग लगती है, यह अमान्य हो जाएगी। फिर आप कॉलबैक में परीक्षण कर सकते हैं कि कोई नया गैर-दोहराने वाला टाइमर बनाया जाना चाहिए, और यदि ऐसा है, तो इसे बनाएं। अवांछित बनाए रखने चक्र तब केवल अगली आग की तारीख तक टाइमर और नियंत्रक जोड़ी रखेगा। 1 सेकंड की आग की तारीख के साथ, आपको चिंता करने की ज़रूरत नहीं होगी।

हर सुझाव लेकिन पहला, बनाए रखने चक्र के साथ रहने का एक तरीका है और उचित समय पर इसे तोड़ने का एक तरीका है। पहला सुझाव वास्तव में बनाए रखने चक्र से बचाता है।


अंदर टाइमर को अमान्य करें - (शून्य) viewWillDisappear: (BOOL) एनिमेटेड मेरे लिए काम किया था


आप कोशिश कर सकते हैं - (void)viewDidDisappear:(BOOL)animated और फिर आपको इसे फिर से सत्यापित करना चाहिए - (void)viewDidAppear:(BOOL)animated

यहां अधिक


इसके आसपास एक तरीका है NStimer को आपके UIViewController के लिए एक कमजोर संदर्भ रखना है। मैंने एक कक्षा बनाई है जो आपके ऑब्जेक्ट के लिए एक कमजोर संदर्भ रखती है और इसके लिए कॉल अग्रेषित करती है:

#import <Foundation/Foundation.h>

@interface WeakRefClass : NSObject

+ (id) getWeakReferenceOf: (id) source;

- (void)forwardInvocation:(NSInvocation *)anInvocation;

@property(nonatomic,assign) id source;

@end

@implementation WeakRefClass

@synthesize source;

- (id)init{
    self = [super init];
//    if (self) {
//    }
    return self;
}

+ (id) getWeakReferenceOf: (id) _source{

    WeakRefClass* ref = [[WeakRefClass alloc]init];
    ref.source = _source; //hold weak reference to original class

    return [ref autorelease];
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    return [[self.source class ] instanceMethodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    [anInvocation    invokeWithTarget:self.source ];

}

@end

और आप इसे इस तरह इस्तेमाल करते हैं:

statusTimer = [NSTimer scheduledTimerWithTimeInterval: 1 target: [WeakRefClass getWeakReferenceOf:self] selector: @selector(updateStatus) userInfo: nil repeats: YES];

आपकी डेलोक विधि को कॉल किया जाता है (पहले के विपरीत) और इसके अंदर आप बस कॉल करते हैं:

[statusTimer invalidate];

मैंने इस कारण से "कमजोर संदर्भ" वर्ग लिखा था। यह NSObject subclasses, लेकिन आगे सभी विधियों कि NSObject एक लक्ष्य वस्तु का समर्थन नहीं करता है। टाइमर कमजोर पड़ता है, लेकिन कमजोर अपने लक्ष्य को बरकरार नहीं रखता है, इसलिए कोई बरकरार चक्र नहीं है।

लक्षित कॉल [कमजोर स्पष्ट] और [टाइमर अमान्य] या तो dealloc में। Icky, है ना?

(अगली स्पष्ट बात यह है कि आप अपनी खुद की टाइमर कक्षा लिखें जो आपके लिए यह सब संभालती है।)


यदि टाइमर.REPEAT YES सेट है, टाइमर के मालिक (जैसे नियंत्रक या दृश्य देखें) टाइमर को अमान्य होने तक हटाया नहीं जाएगा।

इस प्रश्न का समाधान अपने टाइमर को रोकने के लिए कुछ ट्रिगर बिंदु ढूंढना है।

उदाहरण के लिए, मैं एक दृश्य में एनिमेटेड जीआईएफ छवियों को चलाने के लिए टाइमर शुरू करता हूं, और ट्रिगर बिंदु होगा:

  1. जब दृश्य पर्यवेक्षण में जोड़ा जाता है, टाइमर शुरू करें
  2. जब पर्यवेक्षण से दृश्य हटा दिया जाता है, तो टाइमर को रोकें

इसलिए मैं UIView की willMoveToWindow: चयन करता willMoveToWindow: विधि इस प्रकार है:

- (void)willMoveToWindow:(UIWindow *)newWindow {
    if (self.animatedImages && newWindow) {
        _animationTimer = [NSTimer scheduledTimerWithTimeInterval:_animationInterval
            target:self selector:@selector(drawAnimationImages)
            userInfo:nil repeats:YES];
    } else {
        [_animationTimer invalidate];
        _animationTimer = nil;
    }
}

यदि आपका टाइमर व्यू कंट्रोलर के स्वामित्व में है, तो शायद देखें viewWillAppear: और viewWillDisappear: टाइमर शुरू करने और रोकने के लिए आपके लिए एक अच्छी जगह है।





nstimer