6

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

textFieldName.delegate = [NumericTextFieldDelegate new];

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

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

这意味着该对象将在分配后被释放,事实上,当我运行应用程序并关注 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

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

textFieldName.delegate = [NumericTextFieldDelegate getDelegate];

一切正常,但我的问题是:

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

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

谢谢。

4

4 回答 4

2

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

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

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;
}

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

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

@implementaion myTextFieldContainer {
@sythysis delHolder = _delHodler;
...
self.delHolder = [NumericTextFieldDelegate new];
textFieldName.delegate = self.delHolder;

The benefit of above strategy is that you would not worry about release the delegate when your view controller is gone.

于 2012-06-01T01:54:14.960 回答
1

问题是,Cocoa (Touch) 中的代表通常是未保留的。这可以防止保留循环。但这也意味着其他东西需要保留对该对象的引用,以便在您完成它时释放它——否则该对象就会被泄露。这就是委托关系在此模式中的工作方式。

您的getDelegate方法有效的原因是对委托的引用存储在静态变量del中,这使 ARC 无法释放对象。

于 2012-05-31T16:21:52.943 回答
1

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

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

您可以分配该类的新实例。但是它会立即被释放,因为它没有强引用 - 只有一个弱引用(不安全未保留)由 textfield.delegate 来防止已经提到的保留循环。这正是警告告诉你的。但是,我不会使用这种类似单例的模式。只需为您的委托对象添加一个强属性并将该属性值分配为您的文本字段的委托。

@property (nonatomic,strong) MyDelegateObject delegateObject;

Self.delegateObject = [MyDelegateObject new];
Textfield.delegate = self.delegateObject;
于 2012-05-31T20:44:19.650 回答
0

好吧,“为什么”是因为该UITextField属性delegate被声明为:

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

(参见类参考。)

声明的属性assign意味着“setter 使用简单赋值”,因此不实现任何内存管理功能,例如保留(或未分配时释放)。(参见Objective-C 编程语言,声明的属性

于 2012-05-31T18:48:04.093 回答