2012-02-29 13 views
17

मेरे पास एनएसओब्जेक्ट पर एक श्रेणी है जो कुछ सामानों को मानती है। जब मैं इसे किसी ऑब्जेक्ट पर कॉल करता हूं, तो मैं कुछ क्लीनअप करने के लिए अपनी डेलोक विधि को ओवरराइड करना चाहता हूं।एक उदाहरण को स्विजलिंग, कक्षा

मैं इसे विधि स्विजलिंग का उपयोग करके करना चाहता था, लेकिन यह पता नहीं लगा सका। मैंने पाया है कि एकमात्र उदाहरण पूरे वर्ग के लिए विधि कार्यान्वयन को प्रतिस्थापित करने के तरीके पर हैं (मेरे मामले में, यह सभी एनएसओब्जेक्ट्स के लिए डेलोक ओवरराइड करेगा - जो मैं नहीं चाहता)।

मैं NSObject के विशिष्ट उदाहरणों की डेलोक विधि को ओवरराइड करना चाहता हूं।

@interface NSObject(MyCategory) 
-(void)test; 
@end 

@implementation NSObject(MyCategory) 
-(void)newDealloc 
{ 
    // do some cleanup here 
    [self dealloc]; // call actual dealloc method 
} 
-(void)test 
{ 
    IMP orig=[self methodForSelector:@selector(dealloc)]; 
    IMP repl=[self methodForSelector:@selector(newDealloc)]; 
    if (...) // 'test' might be called several times, this replacement should happen only on the first call 
    { 
    method_exchangeImplementations(..., ...); 
    } 
} 
@end 

उत्तर

18

आप वास्तव में ऐसा नहीं कर सकते क्योंकि ऑब्जेक्ट्स की अपनी विधि सारणी नहीं है। केवल कक्षाओं में विधि सारणी होती है और यदि आप उन्हें बदलते हैं तो यह उस वर्ग के हर ऑब्जेक्ट को प्रभावित करेगा। हालांकि इसके आस-पास एक सीधा तरीका है: रनटाइम पर गतिशील रूप से बनाए गए उप-वर्ग में अपनी ऑब्जेक्ट की कक्षा बदलना। इस तकनीक को आईएसए-स्विजलिंग भी कहा जाता है, जिसका उपयोग ऐप्पल द्वारा स्वचालित केवीओ को लागू करने के लिए किया जाता है।

यह एक शक्तिशाली तरीका है और इसका उपयोग है। लेकिन आपके मामले के लिए संबंधित वस्तुओं का उपयोग कर एक आसान तरीका है। मूल रूप से आप objc_setAssociatedObject का उपयोग किसी अन्य ऑब्जेक्ट को अपनी पहली ऑब्जेक्ट में जोड़ने के लिए करते हैं जो dealloc में क्लीनअप करता है। आप this blog post on Cocoa is my Girlfriend में अधिक जानकारी प्राप्त कर सकते हैं।

+0

उत्तर के लिए धन्यवाद। तो मूल रूप से, प्रत्येक कॉल के लिए - मैं एक नई वस्तु आवंटित करता हूं (जिसे मैं कार्यान्वित करता हूं) और इसे संबंधित ऑब्जेक्ट के रूप में सेट करता हूं और मैं अपने डेलोक के अंदर सफाई करता हूं? –

+0

बिल्कुल। उस ब्लॉग पोस्ट को देखें, यहां तक ​​कि 'NSObject' पर भी एक अच्छी श्रेणी है जो कि आप किसी ऑब्जेक्ट के डेलोक के दौरान कॉल करने के लिए ब्लॉक पंजीकृत करते हैं। – Sven

8

विधि चयन किसी ऑब्जेक्ट उदाहरण के वर्ग पर आधारित है, इसलिए विधि swizzling उसी श्रेणी के सभी उदाहरणों को प्रभावित करती है - जैसा आपने पाया था।

लेकिन आप एक उदाहरण की कक्षा बदल सकते हैं, लेकिन आपको सावधान रहना चाहिए! यहाँ रूपरेखा है, मान लें कि एक वर्ग है:

@instance MyPlainObject : NSObject 

- (void) doSomething; 

@end 
अब

अगर सिर्फ MyPlainObject के उदाहरण से कुछ आप आप पहली बार एक उपवर्ग को परिभाषित doSomething के व्यवहार में परिवर्तन करना चाहते हैं के लिए:

@instance MyFancyObject: MyPlainObject 

- (void) doSomething; 

@end 

अब आप स्पष्ट रूप से MyFancyObject के उदाहरण बना सकते हैं, लेकिन हमें MyPlainObject का उदाहरण लेना है और इसे MyFancyObject में बनाना है ताकि हमें नया व्यवहार मिल सके। ऐसी स्थिति में हमने वर्ग धूर्तता कर सकते हैं, जोड़ने MyFancyObject के लिए निम्न:

MyPlainClass *mpc = [MyPlainClass new]; 

... 

// masquerade as MyFancyClass 
[MyFancyClass changeKind:mpc fancy:YES] 

... // mpc behaves as a MyFancyClass 

// revert to true nature 
[MyFancyClass changeKind:mpc: fancy:NO]; 
:

static Class myPlainObjectClass; 
static Class myFancyObjectClass; 

+ (void)initialize 
{ 
    myPlainObjectClass = objc_getClass("MyPlainObject"); 
    myFancyObjectClass = objc_getClass("MyFancyObject"); 
} 

+ (void)changeKind:(MyPlainObject *)control fancy:(BOOL)fancy 
{ 
    object_setClass(control, fancy ? myFancyObjectClass : myPlainObjectClass); 
} 
किसी भी मूल MyPlainClass का उदाहरण आप एक MyFancyClass के रूप में व्यवहार करने के लिए स्विच कर सकते हैं, और इसके विपरीत के लिए

अब

चेतावनियां की (कुछ):

आप केवल अगर उपवर्ग ओवरराइड ऐसा करने या तरीकों कहते हैं कर सकते हैं और कहते हैं +०१२३५७४३५३४(वर्ग) चर।

आपको उस कक्षा के लिए एक उप-वर्ग की भी आवश्यकता है, जिसके लिए आप व्यवहार बदलना चाहते हैं, आपके पास एक भी कक्षा नहीं हो सकती है जो कई अलग-अलग वर्गों के व्यवहार को बदल सकती है।

+0

लेकिन उस स्थिति में, मुझे पहले से पता होना चाहिए कि उस उदाहरण की कक्षा क्या होगी। मेरे मामले में, वस्तु कुछ भी हो सकती है (यहां तक ​​कि एसडीके द्वारा बनाई गई वस्तु भी), इसलिए मुझे नहीं लगता कि मैं इस प्रकार को जानूंगा। –

+0

हां, यह चेतावनी में से एक है! यदि इसकी आवश्यकता है तो संबंधित ऑब्जेक्ट रूट पर जाएं। लेकिन यदि आप विशिष्ट वर्गों को लक्षित करना चाहते हैं तो स्विंगलिंग एक निश्चित (निश्चित रूप से व्यक्तिपरक) संबंधित वस्तुओं की तुलना में सरल है और अधिक सामान्य है क्योंकि आप आसानी से किसी भी विधि को ओवरराइड कर सकते हैं। – CRD

+0

भले ही मैंने अपने समाधान के रूप में एक संबद्ध वस्तु का उपयोग किया है, मैं आपके उत्तर की सराहना करता हूं। मेरे पास इसके बारे में एक सवाल है: चूंकि यह केवल ओवरराइड/विधियों को जोड़ने के लिए उपयोगी है - इस और श्रेणी के बीच क्या अंतर है? –

1

मैंने एक swizzling एपीआई बनाया है जिसमें उदाहरण विशिष्ट स्विजलिंग भी शामिल है।मुझे लगता है कि यह वही है जो आप खोज रहे हैं: https://github.com/JonasGessner/JGMethodSwizzler

यह विशिष्ट उदाहरण के लिए गतिशील सबक्लास बनाकर काम करता है जिसे आप रनटाइम पर घुमा रहे हैं।

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