Directly initialized delegate generates ARC warning and EXC_BAD_ACCESS failure

I created a delegate object that implements UITextFieldDelegate in my class called NumericTextFieldDelegate , after which I initialized the delegate in my controller as follows:

 textFieldName.delegate = [NumericTextFieldDelegate new]; 

And I received this warning from the compiler:

 Assigning retained object to unsafe property; object will be released after assignment 

This means that the object will be released after the assignment, and in fact, when I start the application and I focus on UITextField, I get EXC_BAD_ACCESS and the application crashes ...

The only way I got it working that I found is to create a static variable using the factory method, which sends an instance of NumericTextFieldDelegate :

 @interface NumericTextFieldDelegate : NSObject <UITextFieldDelegate> +(NumericTextFieldDelegate *) getDelegate; @end @implementation NumericTextFieldDelegate - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { NSString *resultingString = [textField.text stringByReplacingCharactersInRange: range withString: string]; // This allows backspace if ([resultingString length] == 0) { return true; } NSInteger holder; NSScanner *scan = [NSScanner scannerWithString: resultingString]; return [scan scanInteger: &holder] && [scan isAtEnd]; } +(NumericTextFieldDelegate *) getDelegate { static NumericTextFieldDelegate *del; @synchronized(del) { if(del == nil) del = [NumericTextFieldDelegate new]; } return del; } @end 

And then when I appoint a delegate this way:

 textFieldName.delegate = [NumericTextFieldDelegate getDelegate]; 

everything works well, but my question is:

Why can't I just assign an anonymous new instance of the class? Why is an object automatically freed up after assignment?

Why do I need this workaround?

Thanks.

+6
source share
4 answers

I agree with the analysis of @Inaziger. The delegate of the UITextField instance is a kind of weak link. It does not contain a delegate assigned to it. According to the ARC, the delegate will be nobody, no one keeps a link to it. Therefore, it will be up to the appointment to save it so that the delegate is called. You will copy the previous workaround like this:

 - (void) somemethod { ... id<UITextFieldDelegate> tempDelegate = [NumericTextFieldDelegate new]; textFieldName.delegate = tempDelegate; ... } 

the textFieldName instance received a link to a delegate created locally in some form. ARC will set temDelegate to zero after calling the method. However, the text field delegate still contains a pointer to the assigned memory, which is subsequently released by ARC. This is why you are facing a bad memory access failure.

By saving del as a static var in your class, it will be stored during the application launch cycle, unless you set it to nil. I think it's better to keep static del as a member of the class and provide a setter so you don't forget to release it. Sort of:

 // in interface definition +(NumericTextFieldDelegate *) getDelegate; +(void) setDelegate:(id)newDel; // in implementation static NumericTextFieldDelegate* del; +(NumericTextFieldDelegate *) getDelegate { @synchronized(del) { if(del == nil) del = [NumericTextFieldDelegate new]; } return del; } +(void) setDelegate:(id)newDel { del = newDel; } 

By the way, you can also save your previous bypass codes as they are. You can save the delegate in the text field class as a variable or class property.

 @interface myTextFieldContainer () { @proerpty (strong) id<UITextFieldDelegate> delHolder; ... } @implementaion myTextFieldContainer { @sythysis delHolder = _delHodler; ... self.delHolder = [NumericTextFieldDelegate new]; textFieldName.delegate = self.delHolder; 

The advantage of the above strategy is that you will not worry about freeing the delegate when your view controller is gone.

+2
source

The fact is that delegates in Cocoa (Touch) are usually not busy. This prevents cycle retention. But it also means that something else needs to be kept in reference to the object in order to free it when you finish with it, otherwise the object will simply leak. This is the same as delegate relationships work in this template.

The reason your getDelegate method getDelegate is because the delegate reference is stored in the static del variable, which allows ARC to free the object.

+1
source

Why can't I just assign an anonymous new instance of the class? Why is an object automatically freed up after assignment?

Why do I need this workaround?

You can assign a new instance of the class. But it is immediately freed because it does not have a strong link - only a weak (unsafe invalid) using textfield.delegate, which is to prevent save cycles, as already mentioned. And that is what warns you. However, I would not use this singleton pattern. Just add a strong property for the delegate object and assign that property value as a delegate to your text field.

 @property (nonatomic,strong) MyDelegateObject delegateObject; Self.delegateObject = [MyDelegateObject new]; Textfield.delegate = self.delegateObject; 
+1
source

Well, the “why” is because the UITextField delegate property is declared as:

 @property(nonatomic, assign) id<UITextFieldDelegate> delegate 

(See link .)

The declared assign property means that "setter uses a simple assignment" and therefore does not implement any memory management functions, such as saving (or freeing when not assigned). (See Objective-C Programming Language, Declared Properties )

0
source

Source: https://habr.com/ru/post/917114/


All Articles