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
__block
modificadores__block
, se convierte en__block
una 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 array
las variables miembro se generan dentro del bloque. La gestión de memoria de esta variable miembro __xxx_block_copy_x
mantiene el objeto externo llamando a la función dentro del bloque __xxx_block_dispose_x
. el objeto.
__strong
Por 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 __block
existirá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 __block
una estructura.Se __block
genera 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 __block
del __forwarding
puntero de estructura (o una copia de sí mismo en el montón) para cambiar __block
el valor dentro de la estructura.
__block
__xxx_block_copy_x
La gestión de memoria de las variables miembro retiene el objeto llamando a la función dentro del Bloque y __xxx_block_dispose_x
libera el objeto.
4. El uso de __block en variables de puntero (objetos) (como: __block NSString *str)
También se generará __block
una estructura y __block
se generará una variable miembro de tipo objeto dentro de la estructura, que hace referencia a un objeto externo.
__block
__Block_byref_id_object_copy_x
La 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_x
libera el objeto.
Nota : Si el objeto externo se
__weak
modifica y__block
modifica,__block
el tipo de objeto correspondiente__weak
se generará dentro de la estructura. Cuando se libera el objeto externo, debido a que el puntero débil se establece automáticamente en nulo,__block
el 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 __block
variables retenidas y modificar los valores de las variables de la forma anterior, pero __forwarding
¿parece redundante la existencia de punteros?
__block
El puntero dentro de la variable __forwarding
permite __block
acceder 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, __block
las variables y el Bloque están en la pila. Cuando el Bloque llama a copy
un método, el Bloque se copia al montón, y las variables llamadas internamente __block
también se copian de la pila al montón y son retenidas por el Bloque. En este momento Al mismo tiempo, __block
las variables tienen variables en la pila, y las variables en el montón, las variables en la pila __block
reemplazarán __forwarding
el valor del puntero de la variable miembro con __block
la 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 __block
la 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) -> __forwarding puntero (apuntando a val en el montón) -> num |
montón | val (montón) -> __forwarding puntero (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
__block
variables en la sintaxis del bloque, fuera de la sintaxis del bloque, o__block
las variables estén configuradas en la pila o el montón, puede acceder a la misma__block
variable 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. copy
Al 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 __strong
una clase con tipo de ID de modificador o a una variable miembro de tipo Bloque
4. Al pasar un bloque en usingBlock
un método o Grand Central Dispatch
API 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 " copy
método de instancia".