2012-06-13 22 views
5

No he usado ARC otra vez que no sea para manejarlo cuando forza el camino a un proyecto a través de un código de terceros. He leído todos los documentos ARC pero no he visto una respuesta a esta pregunta:ARC, no ARC, y herencia

Si tengo una clase definida en un módulo compilado con -fobjc-arc, ¿puedo derivar una nueva clase de esto en un módulo que es NO ARC-habilitado?

En mi opinión, debería funcionar bien siempre que la clase derivada no intente tocar ningún ivars en la clase raíz. Me parece que incluso tener un método dealloc que llame a [super dealloc] estaría bien en la clase derivada.

Y, ¿qué pasa al revés? ¿Puedo derivar una clase habilitada para ARC de una clase que no sea ARC? Debería funcionar bien también, ¿verdad?

Puntos de bonificación: ¿hay algún problema al mezclar códigos ARC y no ARC que debo conocer?

Respuesta

6

No hay problemas de los que tenga conocimiento. Debe darse cuenta de que ARC es algo así como un preprocesador de código fuente, que le agrega las llamadas de administración de memoria durante la compilación. Cuando llegue a la fase de enlace, no podrá distinguir el código ARC del código que no es ARC. (Esta es probablemente una simplificación excesiva, pero una que debería funcionar para sus propósitos.) Si su clase derivada tiene una administración de memoria correcta y la superclase tiene una administración de memoria correcta, el resultado funcionará bien.

La única diferencia que se me ocurre es la manipulación de propiedades weak. Pero no sé lo suficiente acerca de aquellos para decir si es posible llegar al código erróneo usando alguna combinación de código ARC y MRC con propiedades débiles.

+0

este parece ser el consenso. Gracias por tu ayuda. – TomSwift

0

Esto fue un comentario, pero habiéndolo pensado quiero expandir lo que dice.

¿Has probado heredar una clase ARC de una subclase normal? Mis pensamientos (sin haberlo probado tampoco) es que esto no funcionará. En primer lugar, si la clase ARC tiene propiedades públicas o ivars que usan palabras clave ARC, como weak, creo que obtendrá errores durante la compilación del archivo de encabezado. En segundo lugar, no sé cómo funcionaría el dealloc. ¿Necesita llamar al [super dealloc] o no? No lo sé.

De todos modos, si su superclase es ARC, ¿por qué no debería usar ARC en ninguna subclase? No hay ninguna ventaja para hacer eso en absoluto.

¿Puedo obtener una clase habilitada para ARC de una clase que no sea ARC? Debería funcionar bien también, ¿verdad?

Iba a decir que tampoco funcionaría, pero me habría equivocado. Prácticamente todo tiene que heredar de NSObject que es una referencia manual contada.

+0

Tiene sentido derivar de una clase habilitada para ARC en el código MRC, por ejemplo cuando se vincula con una biblioteca que usa ARC y no puede reescribir el proyecto principal. (Lo hago y parece que funciona.) No puede llamar '[super dealloc]' en ARC, creo que el compilador inserta la llamada allí para usted. – zoul

+0

@zoul - sí, este es el escenario que tenemos también; El módulo de terceros requiere ARC, pero no estamos preparados para usar ARC para nuestros propios módulos. – TomSwift

+0

También tiene sentido tener una subclase no ARC de una clase ARC. Específicamente, si una subclase tiene que golpear fuertemente una API de CF, a veces es más fácil hacerlo desde el código MRR que con el código ARC. – bbum

0

Sí, ambos pueden implementar un antecesor que no sea ARC de la clase principal de ARC y antecesor de ARC de una clase no perteneciente a ARC.

En realidad, ARC es un azolve de sintaxis, o puede decir, es solo un preprocesador que analiza el código fuente en el paso de compilación e inserta las llamadas [liberar] y [retener] apropiadas a su código. En el nivel de tiempo de ejecución, nada ha cambiado (excepto las propiedades weak).

0

ARC significa que el compilador se encarga de la gestión de memoria, no ARC significa que cuidar de ella, pero en ambos casos la gestión de memoria funciona exactamente de la misma manera:

  • Si un objeto debe mantenerse con vida, su retener el contador se incrementa (eso es lo que retain hace)
  • Si un objeto ya no es necesaria, su retener contador se disminuye antes de la mención de que se pierda (eso es lo que release hace)
  • Si ha terminado con un objeto, sino no debe morir todavía, por ejemplo como necesita devolverlo como resultado del método (y no desea devolver un objeto muerto), se debe agregar a un grupo de autorrelease que disminuirá su recuento retenido en un momento posterior (eso es lo que autorelease hace, es como diciendo "llame al release sobre ese objeto en el futuro.")
  • Los objetos recién creados tienen un conteo retenido de 1.
  • Si el conteo de retención llega a cero, el objeto se libera.

Si lo hace usted o el compilador lo hace por usted, no juega ningún papel. Después de la compilación, se están llamando a estos métodos, también con ARC, pero con ARC el compilador ha decidido por usted cuándo se llama el método. Hay algo de magia extra, p. ARC no siempre tiene que agregar objetos a los grupos de liberación automática cuando los devuelve como resultado del método, esto a menudo puede optimizarse, pero no tiene que importar, ya que esta magia solo se aplica si el llamante y el método llamado ambos están utilizando ARCO; si uno de ellos no lo es, entonces se usa un autorelease normal (que todavía funciona en ARC exactamente como solía hacerlo).

Lo único que debe cuidar es retener los ciclos. Ya sea que use ARC o no, el recuento de referencias no puede tratar con ciclos de retención. No hay diferencia aquí

Pitfalls? Cuidado con los enlaces gratuitos. Un NSString * y un CFStringRef son de hecho lo mismo, pero ARC no conoce el mundo CF, por lo tanto, mientras ARC se ocupa del NSString, debe ocuparse del CFString. Cuando usa ARC, necesita decirle a ARC cómo hacer un puente.

CFStringRef cfstr = ...; 
NSString * nsstr = (__bridge_transfer NSString *)cfstr; 
// NSString * nsstr = [(NSString *)cfstr autorelease]; 

Código anterior significa "ARC, por favor asegúrese de que la propiedad de CFString objeto y cuidar de soltarlo tan pronto como haya terminado con él". El código se comporta como el código que se muestra en el comentario a continuación; así que cuidado, cfstr debe tener un conteo retenido de al menos uno y ARC lo lanzará al menos una vez, solo que todavía no. A la inversa:

NSString * nsstr = ...; 
CFStringRef cfstr = (__bridge_retained CFStringRef)cftr; 
// CFStringRef cfstr = (CFStringRef)[nsstr retain]; 

Código anterior significa "ARC, por favor, dame la propiedad de que NSString, yo me encargo de liberar una vez que he terminado con ella". ¡Por supuesto, debes mantener esa promesa! En algún momento deberá llamar al CFRelease(cfstr), de lo contrario, perderá memoria.

Finalmente está (__bridge ...) que es solo un tipo de molde, no se transfiere la propiedad. Este tipo de lanzamiento es peligroso, ya que puede crear punteros colgantes si intentas mantener el resultado del lanzamiento. Usualmente lo usas cuando estás alimentando un objeto ARC a una función esperando un objeto CF ya que ARC seguramente mantendrá el objeto vivo hasta que la función regrese, p.esto es siempre seguro:

doSomethingWithString((__bridge CFStringRef)nsstr); 

Incluso si ARC se le permitió liberar nsstr en cualquier momento, no hay código debajo de esa línea cada vez acceda a ella nunca más, que sin duda lo suelte antes de que esta función ha vuelto y argumentos de la función son por definición solo garantiza que permanecerá vivo hasta que la función regrese (en caso de que la función quiera mantener la cuerda viva, debe retenerla y luego ARC no la desasignará después de soltarla ya que el conteo de retención no será cero).

Lo mayoría de las personas parecen tener dificultades con que se pasa ARC objetos como void * contexto, ya que a veces tiene que con la API de más edad, pero que en realidad es absolutamente simple:

- (void)doIt { 
    NSDictionary myCallbackContext = ...; 
    [obj doSomethingWithCallbackSelector:@selector(iAmDone:) 
     context:(__bridge_retained void *)myCallbackContext 
    ]; 
    // Bridge cast above makes sure that ARC won't kill 
    // myCallbackContext prior to returning from this method. 
    // Think of: 
    // [obj doSomethingWithCallbackSelector:@selector(iAmDone:) 
    // context:(void *)[myCallbackContext retain] 
    // ]; 
} 

// ... 

- (void)iAmDone:(void *)context { 
    NSDictionary * contextDict = (__bridge_transfer NSDictionary *)context; 
    // Use contextDict as you you like, ARC will release it 
    // prior to returning from this method. Think of: 
    // NSDictionary * contextDict = [(NSDictionary *)context autorelease]; 
} 

Y tengo que realmente grande tengo para ti que no son tan obvios a primera vista. Tenga en cuenta este código:

@implementation SomeObject { 
    id _someIVAR; 
} 

- (void)someMethod { 
    id someValue = ...; 
    _someIVAR = someValue; 
} 

Este código no es el mismo en ARC y no en ARC. En ARC todas las variables son fuertes por defecto, por lo que en ARC este código se comporta igual que el código tendría:

@interface SomeObject 
    @property (retain,nonatomic) id someIVAR; 
@end 

@implementation SomeObject 

- (void)someMethod { 
    id someValue = ...; 
    self.someIVAR = someValue; 
} 

Asignación someValue lo conservará, el objeto se mantiene vivo! En la no-ARC el código se comportará como éste:

@interface SomeObject 
    @property (assign,nonatomic) id someIVAR; 
@end 

@implementation SomeObject 

- (void)someMethod { 
    id someValue = ...; 
    self.someIVAR = someValue; 
} 

Nota la propiedad es diferente, ya que de Ivar en no-ARC no son ni strong o weak, no son nada, son sólo punteros (en arco que es llamado __unsafe_unretained y la palabra clave aquí es insegura).

Si tiene un código que usa ivars directamente y no usa propiedades con setters/getters para acceder a ellos, entonces cambiar de no ARC a ARC puede provocar que se retengan los ciclos en el código que solía tener una administración de memoria sana. Por otro lado, al pasar de ARC a no ARC, un código como este puede causar punteros colgantes (punteros a objetos anteriores pero dado que el objeto ya ha muerto, estos apuntan a ninguna parte y usarlos tiene resultados impredecibles), como objetos que solían mantenerse vivo antes de que ahora pueda morir inesperadamente.