2011-02-05 18 views
6

acabo de venir a través de un código de Three20 que tiene este aspecto:¿Por qué utilizar performSelector: withObject: withObject en tiempo de ejecución si conoce tanto el selector como sus argumentos en tiempo de compilación?

SEL sel = @selector(textField:didAddCellAtIndex:); 
    if ([self.delegate respondsToSelector:sel]) { 
    [self.delegate performSelector:sel withObject:self withObject:(id)_cellViews.count-1]; 
    } 

En LLVM 2.0, esto hace que el error de compilación:

error: arithmetic on pointer to interface 'id', which is not a constant size in non-fragile ABI

sé por qué ese error está ocurriendo y yo saber cómo solucionarlo Sólo necesito para invocar el método directamente, así:

SEL sel = @selector(textField:didAddCellAtIndex:); 
    if ([self.delegate respondsToSelector:sel]) { 
    [self.delegate textField:self didAddCellAtIndex:(_cellViews.count - 1)]; 
    } 

Mi pregunta es, si sabes tanto el selector y sus argumentos en tiempo de compilación, ¿por qué se necesita para utilizar performSelector:withObject:withObject: en tiempo de ejecución? No veo por qué el código fue escrito de esta manera en primer lugar. Si el selector y los argumentos se pasaron dinámicamente al método, puedo entender, pero no lo están, el selector y sus argumentos están codificados de forma rígida (incluso si el índice cambia durante el tiempo de ejecución, su método para obtener el índice es difícil codificado.)

Si alguien pudiera explicarme una buena razón por la que esto sería necesario, estaría agradecido. De lo contrario, estaré aquí cambiando todo este código.

Respuesta

10

Después de un poco más de excavación, parece que la clase TTPickerTextField en la que se encuentra este código es una subclase indirecta de UITextField.

Como tal, es una copia de seguridad en la propiedad de delegado UITextField s, que no se ajusta al protocolo TTPickerTextFieldDelegate donde se declara el método textField:didAddCellAtIndex:.

He llegado a la conclusión de que este código es simplemente pereza. No hay razón por la que la propiedad de delegado UITextField haya tenido que ser respaldada, lo que hace que este código confuso y propenso a errores sea necesario.

Mi propio enfoque habría sido dejar la propiedad de delegado UITextField s solo, y agregar mi propia propiedad en mi subclase específica que manejaba los métodos de delegado específicos.

Solo para aclarar: la "solución" que mencioné en la pregunta corrige el error del compilador, pero genera una advertencia de que no se puede encontrar el método y se asumirá que devolverá el ID. Esto es lo que el código original estaba 'resolviendo', pero eso solo funcionó en GCC. Ya no con LLVM 2.0.

Última edición, prometo:

Mi solución final para combatir esta pereza y deshacerse de la advertencia y error es un truco feo:

[(id <TTPickerTextFieldDelegate>)self.delegate textField:self didAddCellAtIndex:(_cellViews.count - 1)]; 

moldeada UITextField s delegado a un id que se ajusta a TTPickerTextFieldDelegate y luego invocar el método directamente.

Por favor, no ser perezoso :(

+0

Three20 no debe estar pasando un valor entero como un puntero. Travieso Travieso. Gracias por la corrección. – justice

5

Eso respondsToSelector/performSelector combo es un modismo para los métodos de delegado opcionales. El delegado no está garantizado para tener ese método definido, por lo que una llamada directa a que causaría una advertencia del compilador

Lo que el compilador se queja de hecho, en este caso:

[self.delegate performSelector:sel withObject:self withObject:(id)_cellViews.count-1]; 

error: arithmetic on pointer to interface 'id', which is not a constant size in non-fragile ABI 

es arriesgada aritmética de punteros ... 'id' es un tipo de puntero, por lo que:

(id)_cellViews.count-1 

le dice al compilador que es va a restar uno de un puntero en lugar de un número entero ... que probablemente no es la intención de ese código. El argumento withObject de performSelector debe ser un puntero, no puede ser un primitivo. Puede evitar esto al envolver _cellViews.count - 1 en un NSNumber y desenvolverlo en el método de delegado.

[self.delegate performSelector:sel withObject:self withObject:[NSNumber numberWithInt:_cellViews.count-1]]; 
+0

Sé por qué el compilador se quejaba originalmente, y ese hubiera sido un enfoque mucho mejor para el código original, y probablemente mejor que mi trabajo. El único problema es que este tipo de código está enlazado a través de la biblioteca Three20 y cambiarlo aquí requiere cambiarlo a través de la biblioteca donde se usa. El punto sigue siendo que el código original fue escrito perezosamente y sin ninguna previsión para el futuro. – Jasarien

+0

Cuando no estoy ocupado con mis propias cosas, contribuiré con mi tiempo para solucionar algunos de estos problemas con Three20, pero en este momento, tengo cosas malvadas en mi plato. – Jasarien

Cuestiones relacionadas