2012-05-31 76 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;