Je suis vraiment nouveau dans Swift et je viens de lire que les classes sont passées par référence et que les tableaux/chaînes, etc., sont copiés.
Le passage par référence est-il identique à Objective-C ou Java dans lequel vous transmettez en fait "une" référence ou est-il correct passe par référence?
La règle est la suivante:
Les instances de classe sont types de référence (c'est-à-dire votre la référence à une instance de classe est en réalité un pointeur)
Les fonctions sont des types de référence
Tout le reste est un type de valeur; "tout le reste" signifie simplement des instances de structures et des instances d'énums, parce que c'est tout ce qu'il y a dans Swift. Les tableaux et les chaînes sont des instances de struct, par exemple. Vous pouvez passez une référence à l'un de ces éléments (en tant qu'argument de fonction) en utilisant inout
et en prenant l'adresse, comme l'a souligné newacct. Mais le type est lui-même un type de valeur.
Un objet de type référence est spécial en pratique car:
Une simple affectation ou un passage à une fonction peut donner plusieurs références au même objet
L'objet lui-même est modifiable même si la référence à celui-ci est une constante (let
, qu'elle soit explicite ou implicite).
Une mutation de l'objet affecte cet objet tel qu'il est perçu par toutes les références.
Celles-ci peuvent être dangereuses, alors gardez un œil sur vous. Par contre, passer un type de référence est clairement efficace car seul un pointeur est copié et passé, ce qui est trivial.
Clairement, transmettre un type de valeur est "plus sûr", et let
signifie ce qu'il dit: vous ne pouvez pas transformer une instance struct ou une instance enum via une référence let
. D'autre part, cette sécurité est obtenue en faisant une copie séparée de la valeur, n'est-ce pas? Est-ce que cela ne rend pas le passage d'un type de valeur potentiellement coûteux?
Eh bien oui et non. Ce n'est pas aussi mauvais que vous pourriez le penser. Comme Nate Cook l'a dit, transmettre un type de valeur n'implique pas nécessairement la copie, car let
(explicite ou implicite) garantit l'immutabilité, il n'est donc pas nécessaire de copier quoi que ce soit. Et même passer dans une référence var
ne signifie pas que les choses seront être copiées, mais seulement qu'elles peuvent soient si nécessaire (car il y a une mutation). Les docs vous conseillent spécifiquement de ne pas mettre votre culotte dans une torsion.
Il est toujours transmission par valeur lorsque le paramètre n'est pas inout
.
Il est toujours passage par référence si le paramètre est inout
. Cependant, ceci est quelque peu compliqué par le fait que vous devez utiliser explicitement le &
opérateur sur l'argument lors du passage à un paramètre inout
, de sorte qu'il risque de ne pas correspondre à la définition traditionnelle du passage par référence, dans lequel vous passez directement la variable.
Tout dans Swift est passé par "copie" par défaut. Ainsi, lorsque vous transmettez un type de valeur, vous obtenez une copie de la valeur, et lorsque vous transmettez un type de référence, vous obtenez une copie du référence, avec tout ce que cela implique (c'est-à-dire que la copie de la référence pointe toujours sur la même instance que la référence d'origine).
J'utilise des guillemets autour de la "copie" ci-dessus parce que Swift fait beaucoup d'optimisation; dans la mesure du possible, il ne copie pas avant qu'il y ait une mutation ou la possibilité d'une mutation. Les paramètres étant immuables par Par défaut, cela signifie que la plupart du temps, aucune copie ne se produit réellement.
Voici un petit exemple de code à transmettre par référence. Évitez de le faire, sauf si vous avez une raison forte de le faire.
func ComputeSomeValues(_ value1: inout String, _ value2: inout Int){
value1 = "my great computation 1";
value2 = 123456;
}
Appelez ça comme ça
var val1: String = "";
var val2: Int = -1;
ComputeSomeValues(&val1, &val2);
Le blog Apple Swift Developer -)) est appelé valeur et Types de référence qui fournit une discussion claire et détaillée sur ce sujet même.
Citer:
Les types entre Swift appartiennent à l'une des deux catégories suivantes: premièrement, les "types valeur", où chaque instance conserve une copie unique de ses données, généralement définie comme une structure, une énumération ou un tuple. La seconde , "Types de référence", où les instances partagent une seule copie des données, et le type est généralement défini en tant que classe.
Le billet de blog Swift) continue d’expliquer les différences à l’aide d’exemples et suggère le moment où vous utiliseriez l’un sur l’autre.
Les classes sont transmises par références et les autres par valeur par défaut. Vous pouvez passer par référence en utilisant le mot clé inout
.
Lorsque vous utilisez inout avec un opérateur infixe tel que + =, le symbole & adresse peut être ignoré. Je suppose que le compilateur suppose que passe par référence?
extension Dictionary {
static func += (left: inout Dictionary, right: Dictionary) {
for (key, value) in right {
left[key] = value
}
}
}
origDictionary + = newDictionaryToAdd
Et joliment ce dictionnaire 'add' n'écrit que la référence d'origine aussi, donc super pour le verrouillage!
Classes et structures
L'une des différences les plus importantes entre les structures et les classes est que les structures sont toujours copiées lorsqu'elles sont transmises dans votre code, alors que les classes sont transmises par référence.
Fermetures
Si vous affectez une clôture à une propriété d'une instance de classe et que la fermeture capture cette instance en faisant référence à l'instance ou à ses membres, vous créerez un cycle de référence fort entre la clôture et l'instance. Swift utilise des listes de capture pour rompre ces cycles de référence puissants
ARC (comptage automatique des références)
Le comptage de références s'applique uniquement aux instances de classes. Les structures et les énumérations sont des types valeur, pas des types référence, et ne sont pas stockées et transmises par référence.