Los principios subyacentes de los bloques en iOS

El autor de este artículo se refiere principalmente al libro " Programación avanzada Objective-C: iOS y OS X Multithreading and Memory Management " escrito por Kazuki Sakamoto, Tomohiko Furumoto y traducido por Li Hua. En mi trabajo diario, encuentro mucha confusión relacionada con Block, como: Problema de referencia circular de Block . Entonces, después de leer el libro repetidamente, decidí resumir un artículo relacionado sobre Block. Si aún desea obtener más información sobre Block, el autor recomienda leer los libros anteriores directamente. Debido al nivel limitado del autor, es probable que haya fallas en el artículo y les ruego a ustedes, lectores, que me iluminen. La captura de variables de bloque solo apunta a las variables utilizadas en Block. Las variables no utilizadas no se agregarán a la estructura del lenguaje C, lo que significa que Block no las capturará.
__xxx(外部函数名)_block_impl_x(序号,从0开始)

1. Captura de tipos de variables de valor (como int)

1. Variables globales
  • No se capturará porque se puede acceder a él directamente en la función de transformación de bloques en lenguaje C y se puede usar directamente sin ningún cambio.
2. Variables estáticas globales
  • No se capturará porque se puede acceder a él directamente en la función de transformación de bloques en lenguaje C y se puede usar directamente sin ningún cambio.
3. Variables estáticas locales
  • Capturar , debido a que la variable está fuera del alcance de la función del lenguaje C de la transformación en bloque, se generará un puntero que apunta a la variable y se guardará en la estructura del lenguaje C de la transformación en bloque.
4. Variables locales ordinarias
  • El mecanismo de captura que no captura
    variables estáticas locales parece ser aplicable a la captura de variables locales, pero ¿por qué el funcionario no lo ha hecho?

Permítanme hablar sobre mi entendimiento aquí, 1) Debido a que la intención original de las variables locales de tipos de valores ordinarios es solo usarse en el ámbito local, no quieren que se acceda a ellas desde otros lugares más allá del alcance, por lo que no es necesario. para capturarlos. 2) El propósito de la captura es que cuando la operación se realiza dentro del Bloque, las variables externas también se puedan modificar de la misma forma. Para las variables locales ordinarias de tipo de objeto, como un objeto Array, después de agregarle un Objeto, obviamente también se agregará el Array externo, porque esencialmente apuntan a la misma área de memoria. Para variables locales ordinarias de tipo valor, como tipo int, si queremos modificarlas en el Bloque, la única forma es modificar su valor, pero esto no está permitido. Es necesario agregar modificadores, pero después de agregar los __blockmodificadores __block, se convierte en __blockuna estructura, ya no en un tipo de valor ordinario.

2. Captura de tipo de variable de puntero (objeto) (como: NSString)

Las variables de puntero correspondientes (objetos) se generan dentro de la estructura del bloque , como hacer referencia a objetos de matriz externos, y id __strong arraylas variables miembro se generan dentro del bloque. La gestión de memoria de esta variable miembro __xxx_block_copy_xmantiene el objeto externo llamando a la función dentro del bloque __xxx_block_dispose_x. el objeto.

__strongPor lo tanto, los objetos que usan variables locales con modificadores ( los objetos generados por defecto son de tipo fuerte ) en Block y las variables copiadas al montón __blockexistirán más allá del alcance de sus variables porque están en manos de Block.

Nota : Si el modificador modifica el objeto externo __weak, el objeto de tipo débil correspondiente se generará dentro de la estructura del bloque. Cuando se libera el objeto externo, debido a que el puntero débil se establece automáticamente en nulo, el objeto dentro de la estructura del bloque se establece a nil.nil, aunque se puede realizar cualquier llamada a un método y no provocará que el programa se bloquee, no tendrá ningún resultado. Esto también se usa a menudo para resolver problemas de referencia circular en bloques.

3. El uso de __block para variables de valor (como: __block int val = 0)

Generar __blockuna estructura.Se __blockgenera una variable miembro de tipo valor dentro de la estructura, y su valor es igual al valor de la variable de valor externa. En este momento, cuando se realicen modificaciones dentro del Bloque, se realizarán de la siguiente forma:

	__Block_byref_val_0 *val = __cself->val;

	(val->__forwarding->val) = 1;

Acceda a sí mismo a través __blockdel __forwardingpuntero de estructura (o una copia de sí mismo en el montón) para cambiar __blockel valor dentro de la estructura.

__block__xxx_block_copy_xLa gestión de memoria de las variables miembro retiene el objeto llamando a la función dentro del Bloque y __xxx_block_dispose_xlibera el objeto.

4. El uso de __block en variables de puntero (objetos) (como: __block NSString *str)

También se generará __blockuna estructura y __blockse generará una variable miembro de tipo objeto dentro de la estructura, que hace referencia a un objeto externo.

__block__Block_byref_id_object_copy_xLa gestión de memoria de las variables miembro retiene el objeto llamando a la función dentro del Bloque y __Block_byref_id_object_dispose_xlibera el objeto.

Nota : Si el objeto externo se __weakmodifica y __blockmodifica, __blockel tipo de objeto correspondiente __weakse generará dentro de la estructura. Cuando se libera el objeto externo, debido a que el puntero débil se establece automáticamente en nulo, __blockel objeto dentro de la estructura se establece en nulo. Aunque se puede realizar cualquier llamada a un método y no provocará que el programa falle, no tendrá ningún resultado.

5. __puntero de reenvío de la variable __block

	__Block_byref_val_0 *val = __cself->val;

  	(val->__forwarding->val) = 1;

Dentro del Bloque, puede obtener las __blockvariables retenidas y modificar los valores de las variables de la forma anterior, pero __forwarding¿parece redundante la existencia de punteros?

__blockEl puntero dentro de la variable __forwardingpermite __blockacceder a la variable correctamente independientemente de si la variable está configurada en la pila o en el montón __block. Este es el por qué:

Dominio de almacenamiento de configuración de la variable __block El impacto cuando los bloques se copian de la pila al montón
pila Copiado de la pila al montón y retenido por Block
montón En poder del bloque

He aquí un análisis de una situación.

	__block int val = 0;

	void (^blk)(void) = [ ^{
    
     ++val; } copy ];

	++val;

	blk();

	NSLog(@"%d",val);

Resultado de impresión: 2

Al principio, __blocklas variables y el Bloque están en la pila. Cuando el Bloque llama a copyun método, el Bloque se copia al montón, y las variables llamadas internamente __blocktambién se copian de la pila al montón y son retenidas por el Bloque. En este momento Al mismo tiempo, __blocklas variables tienen variables en la pila, y las variables en el montón, las variables en la pila __blockreemplazarán __forwardingel valor del puntero de la variable miembro con __blockla dirección de la instancia de estructura copiada a la variable en el montón de destino.

Hubo un malentendido aquí y lo supe nuevamente. En este momento, se puede acceder a las variables en la pila/montón al mismo tiempo , pero finalmente se accede a __blockla misma variable. Si no se produce ninguna copia de la variable de la pila al montón, entonces las variables a las que se accede dentro/fuera del bloque pertenecen a la misma variable en la pila y viceversa para acceder a la misma variable en el montón__block__block__block__block__block

Las razones específicas son las siguientes:

Dominio de almacenamiento de configuración de la variable __block Proceso de acceso (después de copiar la variable __block)
pila val (pila) -> __forwardingpuntero (apuntando a val en el montón) -> num
montón val (montón) -> __forwardingpuntero (apuntando a sí mismo en el montón) -> num

Aquí hay una cita original del libro: A través de esta función, ya sea que use __blockvariables en la sintaxis del bloque, fuera de la sintaxis del bloque, o __blocklas variables estén configuradas en la pila o el montón, puede acceder a la misma __blockvariable sin problemas.

6. Momento de copiar el bloque de la pila al montón

Cuando ARC está habilitado, en la mayoría de los casos el compilador hará los juicios apropiados y generará automáticamente código para copiar el bloque de la pila al montón. Se aplican las siguientes situaciones:

1. copyAl llamar al método de instancia de Block

2. Cuando se utiliza Block como valor de retorno de una función o cuando se pasa Block en el parámetro de una función

3. Al asignar Bloque a __stronguna clase con tipo de ID de modificador o a una variable miembro de tipo Bloque

4. Al pasar un bloque en usingBlockun método o Grand Central DispatchAPI del marco Cocoa contenido en el nombre del método

Sin embargo, a veces es necesario copiar manualmente el bloque de la pila al montón, en este caso utilizamos el " copymétodo de instancia".

Supongo que te gusta

Origin blog.csdn.net/weixin_44758107/article/details/127627957
Recomendado
Clasificación