Dois-je toujours copier/Block_copy les blocs en vertu de l'ARC?
Je viens de tombé sur une SORTE de rubrique: Pourquoi doit-on copier des blocs plutôt que de les conserver? qui a la phrase suivante:
Cependant, comme de l'iOS 6, ils sont traités comme des objets de sorte que vous n'avez pas besoin de s'inquiéter.
J'étais vraiment confus par cette affirmation, c'est pourquoi je vous demande: est-ce que cette affirmation impliquent vraiment que Objective-C développeurs n'ont pas besoin de
@property (copy) blockProperties
ou
[^(...){...) {} copy]
pour copier des blocs et de leur contenu à partir de la pile tas plus?
J'espère que la description que j'ai faite est clair.
S'il vous plaît, être commentée.
Des questions similaires
- Les blocs ont TOUJOURS été des objets. Et OS_OBJECT_USE_OBJC n'a rien à voir avec les blocs. Ceux-ci sont pour des choses comme l'expédition des files d'attente.
- Merci, j'ai corrigé ma question.
Vous devez vous connecter pour publier un commentaire.
ARC copie le bloc automatiquement. De clang est Objective-C Automatique De Comptage De Référence documentation:
Si les blocs utilisés uniquement comme des arguments de la fonction ou de la méthode des appels peut rester empiler des blocs, mais sinon, n'importe où que l'ARC conserve le bloc il va copier le bloc. Ceci est mis en œuvre par le compilateur émet un appel à
objc_retainBlock()
, le la mise en œuvre pour ce qui est:C'est toujours une bonne idée de déclarer des propriétés d'un bloc comme ayant la sémantique de copie depuis un bloc attribué à une forte propriété sera en fait être copié. Apple recommande ce ainsi:
Noter que depuis cette copie-sur-conserver la fonctionnalité est fournie par l'ARC, il est dépendant uniquement sur ARC ou ARCLite disponibilité et n'a pas besoin d'un particulier de la version d'OS ou
OS_OBJECT_USE_OBJC
.[myMutableArray addObject: ^ { NSLog(@"%d", i); }];
, la spécification ARC ne garantit pas que le bloc est copié.objc_retainBlock()
sur le bloc argument pour votre scénario, de sorte que l'ARC copie le bloc automatiquement aussi bien.Edit:
Il s'est avéré l'examinant les adresses de la "capturé", les variables sont difficiles à interpréter, et pas toujours appropriée pour déterminer si un Bloc a été copié sur le tas ou se trouve encore sur le tas. Bien que la Spécification de Blocs de données ici BLOC DE SPÉCIFICATION DE MISE EN ŒUVRE, sera suffisante pour décrire les faits, je tente une approche complètement différente:
Qu'est ce qu'un Bloc de toute façon?
Ceci est un résumé de la Spécification officielle BLOC DE SPÉCIFICATION DE MISE EN ŒUVRE:
Un Bloc de code (comme une fonction) et une structure contenant plusieurs morceaux de données, des indicateurs et des pointeurs de fonction ET une variable longueur de la section "capturé variables".
Noter que cette structure est privé et la mise en œuvre définies.
Un Bloc peut être définie en fonction de la portée, où cette structure est créée dans la pile de la mémoire locale, ou elle peut être définie dans globales ou statiques portée, où la structure est créée en mémoire statique.
Un Bloc peut "importer" autres références de Bloc, les autres variables et
__block
les variables modifiées.Lorsqu'un Bloc de références à d'autres variables, ils seront importés:
Une pile locale (automatique) de la variable, va être "importés" par les moyens de rendre un "const copie".
Un
__block
modifié variable sera importé par le biais de l'affectation d'un pointeur l'adresse de cette variable enfermé dans une autre structure.Variables globales seront simplement référencé (et non pas "importés").
Si une variable sera importé, le "capturé variable" vie dans la structure susmentionnée dans la variable longueur de la section. C'est, la "contrepartie" de la variable automatique (qui vit à l'extérieur du bloc) dispose d'un stockage à l'intérieur du bloc de la structure.
Depuis cette "capturé" variable est en lecture seule, le compilateur peut appliquer quelques optimisations: par exemple, nous avons vraiment seulement besoin un exemple de la capture de variable sur le tas, si jamais nous avons besoin d'une copie de la bloquer.
La valeur de la capture de variables sera définie lorsque le bloc littérale de l'expression sera évaluée. Cela implique également que le stockage de la capture de variables doit être accessible en écriture (qui est, pas de section de code).
La durée de vie de la capture de variables est une fonction: chaque invocation de ce bloc, il faudra une nouvelle copie de ces variables.
Capturé variables dans les Blocs qui vivent sur la pile sont détruits lorsque le programme des feuilles de l'instruction composée du bloc.
Capturé variables dans les Blocs qui vivent sur le tas sont détruits lorsque le bloc sera détruit.
Selon le Bloc de l'API, les versions 3.5:
D'abord, lorsqu'un bloc littérale est créé, cette structure existe sur la pile:
Veuillez noter que c'est pour Bloc littéraux.
Selon la Objective-C Extensions de Blocs, le compilateur va traiter les Blocs comme objets.
Observations
Maintenant, il est difficile de trouver un code de test qui prouve à ces affirmations. Ainsi, il semble préférable d'utiliser le débogueur et le jeu symbolique des points d'arrêt sur les fonctions de
_Block_copy_internal
etmalloc
(qui doit être activé uniquement après le premier point d'arrêt a été frappé)et exécutez ensuite adapté le code de test (comme les extraits de code ci-dessous) et d'examiner ce qui se passe:
Dans l'extrait de code suivant, nous créons un Bloc littéral et le passer à travers comme paramètre d'une fonction qui l'appelle:
La sortie est comme suit:
L'adresse de la "capturé" variable
x0
indique fortement qu'il vit sur la pile.Nous avons également définir un point d'arrêt à
_Block_copy_internal
- toutefois, il ne sera pas touché. Ceci indique que le Bloc n'a pas été copié sur le tas. Une autre preuve peut être faite avec des Instruments, qui ne montre pas les allocations en fonctionfoo
.Maintenant, si nous avons de créer et d'initialiser un bloc variable, paraît-il, le Bloc de données de la structure de l'original Bloc littérale initialement créé sur la pile, seront copiées sur le tas:
Au-dessus de cette copies le bloc qui a été initialement créé sur la pile. Cela peut tout simplement être dû à l'ARC et le fait que nous avons un bloc littéral sur la main droite, et le bloc variable
block
sur la main gauche sera attribué le bloc littérale - ce qui résulte en uneBlock_copy
opération. Cela rend les Blocs apparaissent bien comme d'habitude Objective-C objets.Les Allocations tracée avec des Instruments similaires de code ci-dessous
montrent, que le Bloc sera en effet copié:
À noter
Lorsqu'un bloc ne rend pas compte de toutes les variables, c'est comme une fonction normale. Ensuite, il est préférable d'appliquer une optimisation, où un
Block_copy
opération ne fait rien, car il n'y a rien à copier. clang la met en œuvre avec prise de ces blocs globale "bloc".Lors de l'envoi de
copy
à un bloc de variables et affectation du résultat à une autre variable d'un bloc, par exemple:copy
sera un très pas cher opération, puisque le blocblock
a déjà de stockage alloué pour le bloc de la structure qui peut être partagé. Ainsi,copy
n'a pas besoin d'allouer du stockage de nouveau.Revenir à la question
Pour répondre à la question de savoir si nous devons explicitement copie d'un bloc, dans certaines circonstances, par exemple:
Bien, une fois que le bloc a été copié, n'importe quand, et sera ensuite assigné à la variable cible (un Bloc de variables), nous devrions être toujours en sécurité, sans explicitement la copie du bloc, puisqu'il est déjà dans le tas.
Dans le cas du bloc propriété, il doit être sûr si nous serait tout simplement wright:
et puis:
Ce devrait être à l'abri depuis l'attribution d'un bloc littéral (qui vit sur la pile) le bloc sous-jacent variable
_completion
, permet de copier le bloc sur le tas.Néanmoins, je voudrais encore vous recommandons d'utiliser l'attribut
copy
- puisqu'il est encore suggérée par les documents officiels comme les meilleures pratiques, et prend également en charge les anciennes Api où les Blocs ne se comportent pas comme d'habitude Objective-C objets et où il n'y a pas d'ARC.Système existant Api permettra aussi de prendre soin de la copie d'un Bloc si nécessaire:
dispatch_async() va faire de la copie pour nous. Donc, nous n'avons pas à s'inquiéter.
D'autres scénarios sont plus subtiles:
Mais en réalité, c'est sûr: Le bloc littérale sera copié et assigné à la variable bloc
block
.Cet exemple peut même paraître effrayant:
Mais il semble, le Bloc littérale sera correctement copié comme un effet de l'attribution de l'argument (le Bloc) pour le paramètre (un
id
) dans la méthodearrayWithObject:
qui copie le Bloc littérale sur le tas:En outre,
NSArray
sera conserver l'objet passé en paramètre de la méthodearrayWithObject:
. Cela provoque un autre appel àBlock_copy()
cependant, puisque le Bloc est déjà sur le tas, cet appel n'a pas allouer de l'espace de stockage.Cela indique qu'un "conserver" un message envoyé à un Bloc littérale quelque part dans la mise en œuvre de
arrayWithObject:
serait également en effet de copier le Bloc.Donc, quand avons-nous réellement besoin explicitement copier un Bloc?
Un bloc littérale - par exemple, l'expression:
permettra de créer la structure du bloc sur la pile.
Donc, nous avons fait besoin de faire un copier uniquement dans les cas où nous avons besoin de le Bloquer sur le tas et si cela n'arrive pas "automatiquement". Cependant, il n'y a pratiquement pas de scénarios où c'est le cas. Même dans les cas où nous passons d'un Bloc comme un paramètre à une méthode qui n'a aucune idée de qui il s'agit d'un Bloc littérale et nécessite un
copy
(par exemple:arrayWithObject:
), et, éventuellement, le récepteur envoie juste un conserver message à l'objet donné en paramètre, le Bloc sera copié sur le tas en premier.Exemples où l'on peut utiliser explicitement
copy
, est l'endroit où nous n'avons pas attribuer un Bloc littérale à une variable d'un bloc ou d'unid
, et donc de l'ARC ne peut pas comprendre qu'il a à faire une copie du bloc de l'objet ou a envoyer "conserver".Dans ce cas, l'expression
permettra d'obtenir un autoreleased Bloc dont la structure se trouve sur le tas.
&block
, pas le bloc lui-même. Depuis les blocs sont Obj-C des objets, ils sont des pointeurs.Mine réponse originale à cette question était mal.
Édité réponse n'est pas une réponse, mais plus d'un "c'est en effet une bonne question" chose.
S'il vous plaît, voir Matt réponse pour les références pourquoi des choses est la façon dont il est.
Après avoir testé le code suivant sur iOS7:
J'ai obtenu ceci:
Qui signifie en gros que les blocs créés et affectés dans la pile des variables sont en fait déjà sur un segment et la copie n'a pas d'incidence sur quoi que ce soit.
Cela, cependant, n'est pas étayée par aucune source officielle, comme ils ont encore de l'état que les blocs sont créés sur la pile doivent être copiés "lors du passage vers le bas".
Partie de la réponse avant que j'ai testé, indiquant les docs sont contredites par l'exemple.
Document sur la transition à l'ARC états:
Et docs sur les blocs et les propriétés dit:
copy
.copy
est toujours fort, mais pas toujours nécessaire, dans le cas où le Bloc est déjà sur le tas. (La copie est en fait qu'une "conserver" lorsque le Bloc est sur le tas déjà).copy
.