2012-08-09 22 views
10

मेरे पास मेरे कोड में एआरसी ऑटो-डालने objc_retains से संबंधित यह अजीब क्रैश है।प्रदर्शन से चयन विधि में objc_retain में क्रैश

मैं निम्नलिखित दो वर्गों है:

@interface MenuItem : NSObject 
@property (weak, nonatomic) id target; 
@property (unsafe_unretained, nonatomic) SEL action; 
@property (strong, nonatomic) id object; 
- (instancetype)initWIthTarget:(id)target action:(SEL)action withObject:(id)object; 
- (void)performAction; 
@end 

@implementation MenuItem 
- (void)performAction 
{ 
    if (self.target && self.action) 
    { 
     if (self.object) 
     { 
     [self.target performSelector:self.action withObject:self.object]; 
     } 
     else 
     { 
     [self.target performSelector:self.action]; 
     } 
    } 
} 
@end 

@interface Widget : NSObject 
- (void)someMethod:(id)sender; 
@end 

कुछ बिंदु पर मैं इस तरह के रूप में एक MenuItem का दृष्टांत:

MenuItem *item = [MenuItem alloc] initWithTarget:widget action:@selector(someMethod:) object:nil]; 

तब कहीं मैं मेनू आइटम पर performAction आह्वान:

[item performAction]; 

someMethod के कार्यान्वयन में मुझे एक क्रैश मिलता है:

@implementation Widget 
- (void)someMethod:(id)sender 
{ 
    // EXEC_BAD_ACCESS crash in objc_retain 
} 
@end 

ऐसा क्यों हो रहा है?

उत्तर

17

दुर्घटना का कारण था क्योंकि मैं गलत performSelector का उपयोग कर रहा था।

NSObjectperformSelector के कई संस्करणों को परिभाषित करता है। एक मैं लागू किया गया था:

- (id)performSelector:(SEL)aSelector; 

हालांकि विधि मैं लागू किया गया था एक id पैरामीटर ले लिया। उदाहरण के लिए:

- (void)someMethod:(id)sender; 

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

- (void)someMethod:(id)sender 
{ 
    objc_retain(sender); 
    objc_release(sender); 
} 

समस्या इस के साथ तथापि था कि मैं performSelector: लागू किया गया था और sender पैरामीटर के लिए एक मूल्य की आपूर्ति नहीं। तो sender स्टैक पर यादृच्छिक जंक पर इशारा कर रहा था। इसलिए जब objc_retain() को ऐप क्रैश किया गया था।

अगर मैं बदलने के लिए:

MenuItem *item = [[MenuItem alloc] initWithTarget:widget 
              action:@selector(someMethod:) 
              object:nil]; 

MenuItem *item = [[MenuItem alloc] initWithTarget:widget 
              action:@selector(someMethod) 
              object:nil]; 

और

- (void)someMethod:(id)sender; 

करने के लिए

- (void)someMethod; 

तो दुर्घटना दूर हो जाती है।

इसी तरह मैं भी बदल सकते हैं

[self.target performSelector:self.action]; 

अगर मैं लक्ष्य कार्रवाई तरीकों कि एक एकल पैरामीटर लेने के 'मानक' प्रपत्र का पालन करना चाहते

को
[self.target performSelector:self.action withObject:nil]; 

performSelector के दूसरे रूप का लाभ यह है कि यदि मैं ऐसी विधि का आह्वान कर रहा हूं जो पैरामीटर नहीं लेता है तो यह अभी भी ठीक काम करेगा।