直接初始化的委托生成ARC警告和EXC_BAD_ACCESS崩溃

ale*_*oot 6 delegates objective-c automatic-ref-counting

我创建了一个委托对象,在其自己的类中实现了UITextFieldDelegate,NumericTextFieldDelegate然后我以这种方式在我的控制器中初始化了委托:

textFieldName.delegate = [NumericTextFieldDelegate new];
Run Code Online (Sandbox Code Playgroud)

我从编译器得到了这个警告:

Assigning retained object to unsafe property; object will be released after assignment
Run Code Online (Sandbox Code Playgroud)

这意味着该对象将在赋值后释放,事实上,当我运行应用程序时,我关注UITextField,我得到了一个EXC_BAD_ACCESS和应用程序崩溃...

使我找到的工作的唯一方法是使用工厂方法创建一个静态变量,该方法调度以下实例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
Run Code Online (Sandbox Code Playgroud)

然后当我以这种方式分配代表时:

textFieldName.delegate = [NumericTextFieldDelegate getDelegate];
Run Code Online (Sandbox Code Playgroud)

一切运作良好,但我的问题是:

为什么我不能简单地分配一个匿名的新类实例? 为什么在分配后自动释放对象?

为什么我需要这个解决方法?

谢谢.

Dav*_*.ca 2

我同意@Inaziger 的分析。UITextField实例的委托是一种弱引用。它不持有分配给它的委托。根据 ARC,如果没有人持有该委托的引用,该委托将为零。因此,由分配者保留它,以便调用委托。您编写的先前解决方法是这样的:

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

textFieldName 的实例获得了对以某种方式本地创建的委托的引用。方法调用后,ARC 会将 temDelegate 设置为 nil。但是,文本字段的委托仍然保留指向分配给的内存的指针,该指针随后由 ARC 释放。这就是为什么你会遇到严重的内存访问崩溃。

通过将 del 保留为类中的静态变量,只要您没有将其设置为 nil,它就会在您的应用程序运行周期中保留。我认为最好将静态 del 保留为类级别成员并提供一个 setter,以便您应该记住释放它。就像是:

// 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;
}
Run Code Online (Sandbox Code Playgroud)

顺便说一句,您还可以保留之前的解决方法代码不变。您可以将委托保留在文本字段的类中作为类成员变量或属性。

@interface myTextFieldContainer () {
@proerpty (strong) id<UITextFieldDelegate> delHolder;
...
}

@implementaion myTextFieldContainer {
@sythysis delHolder = _delHodler;
...
self.delHolder = [NumericTextFieldDelegate new];
textFieldName.delegate = self.delHolder;
Run Code Online (Sandbox Code Playgroud)

上述策略的好处是,当视图控制器消失时,您不必担心释放委托。