2012-05-31 5 views
6

ich einen Delegaten Objekt erstellt habe die UITextFieldDelegate in seiner eigenen Klasse Implementierung namens NumericTextFieldDelegate dann habe ich die Delegaten in meinem Controller auf diese Weise initialisiert:direkt initialisiert Delegaten erzeugen ARC Warnung und EXC_BAD_ACCESS Absturz

textFieldName.delegate = [NumericTextFieldDelegate new]; 

Und ich habe diese Warnung vom Compiler:

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

das bedeutet, dass das Objekt nach der Abtretung und in der Tat freigegeben werden, wenn ich die Anwendung auszuführen, und ich konzentriere, um den UITextField ich erhalte ein EXC_BAD_ACCESS ein nd der App Absturz ...

Der einzige Weg, es funktioniert, dass ich gefunden wird eine statische Variable mit einer Factory-Methode zu schaffen, die die Instanz der NumericTextFieldDelegate Versand:

@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 

Und dann, wenn ich zuweisen die Delegierten auf diese Weise:

textFieldName.delegate = [NumericTextFieldDelegate getDelegate]; 

alles funktioniert gut, aber meine Frage ist:

Warum kann ich nicht einfach eine anonyme neue Instanz der Klasse zuweisen? Warum wird das Objekt nach der Zuweisung automatisch freigegeben?

Warum brauche ich diese Problemumgehung?

Danke.

+0

Es ist ein UITextField, nicht benutzerdefiniert. – aleroot

+1

Methoden sollten nicht mit "get" vorangestellt werden, es sei denn, sie sind von einem sehr spezifischen Typ (was dieser nicht ist). – bbum

+0

@bbum ich habe das get Präfix von Java geerbt :-) Danke für den Tipp – aleroot

Antwort

2

Ich stimme @Inaziger Analyse. Der Delegat der UITextField-Instanz ist eine Art schwache Referenz. Es hält den ihm zugewiesenen Delegaten nicht. Laut ARC wird der Delegierte niemanden haben, der einen Hinweis darauf hält. Daher ist es Aufgabe des Zuweisenden, es zu behalten, damit der Delegierte aufgerufen wird. Sie Code vor Abhilfe ist so etwas wie dieses:

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

die Instanz von textFieldName bekam einen Verweis auf einen Delegierten vor Ort in somethod erstellt. ARC wird temDelegate nach dem Methodenaufruf auf null setzen. Der Delegat des Textfelds enthält jedoch immer noch den Zeiger auf den zugewiesenen Speicher, der anschließend von ARC freigegeben wird. Aus diesem Grund haben Sie den Absturz des Speicherzugriffs bekommen.

Wenn Sie del als statische Variable in Ihrer Klasse beibehalten, wird sie während des Laufs Ihrer App beibehalten, solange Sie sie nicht auf Null gesetzt haben. Ich denke, es ist besser, die statische del als Mitglied auf Klassenebene zu behalten und einen Setter bereitzustellen, so dass Sie daran denken sollten, ihn zu veröffentlichen. Etwas wie:

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

Übrigens können Sie auch Ihre früheren Workaround-Codes behalten, wie sie sind. Sie können den Delegaten in der Klasse des Textfelds als Klassenmitgliedsvariable oder -eigenschaft beibehalten.

Der Vorteil der oben genannten Strategie ist, dass Sie sich keine Sorgen über die Freigabe der Stellvertretung machen würden, wenn Ihr View-Controller weg ist.

1

Die Sache ist, Delegaten in Cocoa (Touch) sind normalerweise nicht erhalten. Dies verhindert Rückhaltezyklen. Das bedeutet aber auch, dass etwas anderes einen Verweis auf das Objekt behalten muss, um es freizugeben, wenn Sie damit fertig sind - sonst ist das Objekt nur geleakt. So funktioniert die Delegatenbeziehung in diesem Muster.

Der Grund, warum Ihre getDelegate-Methode funktioniert, liegt daran, dass eine Referenz auf den Delegaten in der statischen Variablen del gespeichert ist, die ARC davon abhält, das Objekt freizugeben.

+0

Ja, ich weiß, weil es funktioniert ... Ich habe den Workaround selbst geschrieben. Was ich nicht verstehen kann ist, warum, wenn in ARC kann ich das Objekt nicht behalten, dass diese Art der Zuweisung nicht funktioniert ... – aleroot

0

Nun, das "Warum", weil die UITextField Eigenschaft delegate deklariert als:

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

(die class reference See.)

Die deklarierte Eigenschaft assign bedeutet, dass die "Setter einfache Zuordnung verwendet" und implementiert daher keine Speicherverwaltungsfunktionen wie beibehalten (oder freigeben, wenn es nicht zugewiesen ist). (Siehe The Objective-C Programming Language, Declared Properties)

+0

Ja, aber warum ARC "nicht" dies versteht? Warum muss ich umgehen? – aleroot

+0

Es versteht es, aber gibt Ihnen die genauen Ergebnisse, die angefordert wurden. Es stellt sich heraus, dass es wahrscheinlich nicht das ist, was du willst **, also gibt dir eine Warnung. Die bessere Frage wäre "Warum verwendet Apple für ihre Delegierten Zuweisungen?". Das hat mit der Vermeidung von Rückhaltezyklen zu tun, würde ich wagen zu erraten. – lnafziger

1

Warum kann ich nicht einfach eine anonyme neue Instanz der Klasse zuweisen? Warum wird das Objekt nach der Zuweisung automatisch freigegeben?

Warum brauche ich diese Problemumgehung?

Sie können eine neue Instanz der Klasse zuweisen. Aber es wird sofort freigegeben, weil es keine starke Referenz gibt - nur eine schwache (unsichere nicht) von Textfeld.delegieren, die Retain-Zyklen wie bereits erwähnt verhindern soll. Und genau das sagt dir die Warnung. Ich würde dieses Singleton-ähnliche Muster jedoch nicht verwenden. Fügen Sie einfach eine strong-Eigenschaft für Ihr Delegatobjekt hinzu und weisen Sie diesen Eigenschaftswert als Stellvertreter für Ihr Textfeld zu.

@property (nonatomic,strong) MyDelegateObject delegateObject; 

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