2012-05-31 13 views
6

He creado un objeto delegado de la aplicación de la UITextFieldDelegate en su propia clase llamada NumericTextFieldDelegate entonces he inicializado el delegado en mi controlador de esta manera:delegado directamente inicializado genera la advertencia ARC y EXC_BAD_ACCESS accidente

textFieldName.delegate = [NumericTextFieldDelegate new]; 

y yo nos esta advertencia del compilador:

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

eso significa que el objeto se dará a conocer después de la asignación y de hecho cuando ejecuto la aplicación y se enfoca el UITextField me sale un EXC_BAD_ACCESS una nd el accidente aplicación ...

La única manera de hacer que funcione que he encontrado es la creación de una variable estática con un método de fábrica que despachar la instancia de la 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 

Y luego, cuando le asigno el delegado de esta manera:

textFieldName.delegate = [NumericTextFieldDelegate getDelegate]; 

todo funciona bien, pero mi pregunta es:

por qué no puedo simplemente asignar una nueva instancia anónima de la clase? ¿Por qué el objeto se libera automáticamente después de la asignación?

¿Por qué necesito esta solución?

Gracias.

+0

Es un UITextField, no personalizado. – aleroot

+1

Los métodos no deben ir precedidos de 'get' a menos que sean de un tipo muy específico (que este no es). – bbum

+0

@bbum heredé el prefijo get de Java :-) Gracias por la sugerencia – aleroot

Respuesta

2

Estoy de acuerdo con @Inaziger analysis. El delegado de la instancia UITextField es una especie de referencia débil. No tiene el delegado asignado. Según ARC, el delegado será nulo, nadie sostiene una referencia al mismo. Por lo tanto, le corresponderá a la persona encargada conservarlo para que se llame al delegado. Usted código de solución antes es algo como esto:

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

la instancia de textFieldName tiene una referencia a un delegado creado localmente en somethod. ARC configurará temDelegate en nil después de la llamada al método. Sin embargo, el delegado del campo de texto aún mantiene el puntero a la memoria asignada, que luego ARC publica. Es por eso que tienes un fallo en el acceso a la memoria.

Al mantener el del como var estático en su clase, se mantendrá durante el ciclo de ejecución de la aplicación, siempre que no lo haya establecido en cero. Creo que es mejor mantener el static del como miembro de nivel de clase y proporcionar un setter para que recuerde liberarlo. Algo como:

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

Por cierto, también puede mantener sus códigos de solución anteriores tal como están. Puede mantener al delegado en la clase del campo de texto como una propiedad o variable miembro de clase.

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

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

El beneficio de la estrategia anterior es que usted no se preocupe por la liberación del delegado cuando el controlador de vista se ha ido.

1

El hecho es que los delegados en Cocoa (Touch) normalmente no están retenidos. Esto evita los ciclos de retención. Pero esto también significa que algo más necesita mantener una referencia al objeto para poder liberarlo cuando haya terminado con él; de lo contrario, el objeto acaba de filtrarse. Esa es la forma en que la relación de delegados funciona en este patrón.

El motivo por el que su método getDelegate funciona es porque una referencia al delegado se almacena en la variable estática del, que evita que ARC libere el objeto.

+0

Sí, lo sé porque funciona ... He escrito la solución por mi cuenta. Lo que no puedo entender es por qué si en ARC no puedo conservar el objeto, ese tipo de asignación no funciona ... – aleroot

0

Bueno, el "por qué" es porque la propiedad delegateUITextField se declara como:

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

(Véase el class reference.)

La propiedad declarada assign significa que el "colocador utiliza asignación simple" y por lo tanto no implementa ninguna característica de administración de memoria como retener (o liberar cuando no está asignada). (Consulte The Objective-C Programming Language, Declared Properties)

+0

Sí, pero ¿Por qué ARC no "entiende" esto? ¿Por qué tengo que solucionarlo? – aleroot

+0

Lo comprende, pero le proporciona los resultados exactos que se solicitaron. Sin embargo, se da cuenta de que probablemente no sea lo que ** quieres **, así que te da una advertencia. La mejor pregunta sería "¿Por qué Apple utiliza asignar para sus delegados?". Esto tiene que ver con evitar los ciclos de retención, me atrevería a adivinar. – lnafziger

1

¿Por qué no puedo simplemente asignar una nueva instancia anónima de la clase? ¿Por qué el objeto se libera automáticamente después de la asignación?

¿Por qué necesito esta solución?

Puede asignar una nueva instancia de la clase. Pero se libera inmediatamente porque no tiene una referencia fuerte, solo una débil (no segura no retenida) por campo de texto.delegado que es para evitar retener ciclos como ya se mencionó. Y eso es exactamente lo que la advertencia te está diciendo. Sin embargo, no usaría este patrón tipo singleton. Simplemente agregue una propiedad fuerte para su objeto delegado y asigne ese valor de propiedad como delegado de su campo de texto.

@property (nonatomic,strong) MyDelegateObject delegateObject; 

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