2012-05-31 47 views
6

我已经创建实现在一个名为NumericTextFieldDelegate然后它自己的类UITextFieldDelegate委托对象我已经初始化委托我这样的控制器:直接初始化代表产生ARC警告和EXC_BAD_ACCESS崩溃

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

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

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

为什么我需要此解决方法?

谢谢。

+0

这是一个UITextField,而不是自定义。 – aleroot

+1

方法不应该以'get'作为前缀,除非它们是非常具体的类型(这不是这个类型)。 – bbum

+0

@bbum我从Java继承了get前缀:-)感谢您的提示 – aleroot

回答

2

我同意@Inaziger分析。 UITextField实例的委托是一种弱引用。它不包含分配给它的委托。根据ARC,代表将是零,没有人提到它。因此,由分配者来保存它,这样代表将被调用。您的代码事先解决方法是这样的:

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

textFieldName的实例获得了对方法在本地创建的委托的引用。在方法调用之后,ARC会将temDelegate设置为零。但是,文本字段的委托仍然保存指向内存的指针,之后由ARC发布。这就是为什么你的内存访问崩溃的原因。

通过将del作为静态var保存在您的类中,只要您尚未将其设置为零,就会在您的应用运行周期中保留它。我认为最好将静态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; 

上述策略的好处是,当你的视图控制器不见了的时候,你不会担心释放委托。

1

事情就是,可可(Touch)中的代表通常都是未保留的。这可以防止保留周期。但是这也意味着其他东西需要保持对该对象的引用,以便在完成时释放它 - 否则该对象只是被泄漏。这就是委托关系在这种模式下工作的方式。

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

+0

是的,我知道,因为它的作品...我自己写了解决方法。我不明白的是,为什么如果在ARC中我不能保留那种类型的赋值不起作用的对象... – aleroot

0

那么,“为什么”是因为UITextField财产delegate声明为:

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

(见class reference

申报财产assign意味着“二传手使用简单的任务”因此不会实现任何内存管理功能,如保留(或未分配时释放)。 (见The Objective-C Programming Language, Declared Properties

+0

是的,但是为什么ARC不“理解”这个?为什么我必须解决方法? – aleroot

+0

它的确了解它,但它能提供你所要求的确切结果。它认为这可能不是你想**的,所以给你一个警告。更好的问题是“为什么苹果使用分配给他们的代表?”。这与避免保留周期有关,我敢冒险猜测。 – lnafziger

1

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

为什么我需要此解决方法?

您可以指定一个新的类实例。但它会立即被释放,因为它没有强大的参考 - 只有文本字段很弱(不安全)。如前所述,这是为了防止保留周期的代表。这正是警告告诉你的。 但是,我不会使用这种单例模式。只需为委托对象添加一个强大的属性并将该属性值指定为文本字段的委托。

@property (nonatomic,strong) MyDelegateObject delegateObject; 

Self.delegateObject = [MyDelegateObject new]; 
Textfield.delegate = self.delegateObject;