() translation by (you can also view the original English article)
No artigo anterior, exploramos os conceitos básicos de funções em Swift. Funções, no entanto, têm muito mais a oferecer. Neste artigo vamos continuar nossa exploração de funções e ver parâmetros, encadeamento e tipos de funções.
1. Nomes de parâmetros local e externo
Nomes de Parâmetro Local
Vamos revisar um dos exemplos do artigo anterior. A função printMessage
define um parâmetro, message
.
1 |
func printMessage(message: String) { |
2 |
println(message) |
3 |
}
|
Mesmo definindo um nome para o parâmetro, message
, não usamos o nome quando chamamos a função. Passamos o valor para o parâmetro message
.
1 |
printMessage("Hello, world!") |
O nome que definimos na definição da função é um nome local do parâmetro. O nome do parâmetro so poderá ser referenciado por este nome no corpo da função. Parâmetros de funções, porém, são um pouco mais flexíveis que isso. Deixe-me explicar o que quero dizer com isso.
O Objective-C é conhecido por seus nomes de método longos. Apesar disto parecer desajeitado e deselegante a estranhos, faz com que os métodos sejam fáceis de compreender e, se bem escolhido, muito descritivo. Se você acha que perdeu este benefício quando mudou para o Swift, então você terá uma surpresa.
Nomes de parâmetros externo
Quando uma função aceita vários parâmetros, nem sempre é óbvio qual argumento corresponde a qual parâmetro. Dê uma olhada no exemplo a seguir para entender melhor o problema.
1 |
func power(a: Int, b: Int) -> Int { |
2 |
var result = a |
3 |
|
4 |
for _ in 1..<b { |
5 |
result = result * a |
6 |
}
|
7 |
|
8 |
return result |
9 |
}
|
A função power
eleva o valor de a
pelo expoente b
. Ambos os parâmetros são do tipo Int
. Enquanto a maioria das pessoas intuitivamente vai passar o valor de base como o primeiro argumento e o expoente como o segundo argumento, não é evidente a partir do tipo da função, nome ou assinatura. Como já vimos no artigo anterior, chamar a função é simples.
1 |
power(2, 3) |
Para evitar confusão, podemos dar aos parâmetros de uma função nomes externos. Podemos usar estes nomes externos quando a função é chamada para indicar inequivocamente qual argumento corresponde a qual parâmetro. Dê uma olhada no exemplo atualizado abaixo.
1 |
func power(base a: Int, exponent b: Int) -> Int { |
2 |
var result = a |
3 |
|
4 |
for _ in 1..<b { |
5 |
result = result * a |
6 |
}
|
7 |
|
8 |
return result |
9 |
}
|
Observe que o corpo da função não muda desde que os nomes locais não mudem. No entanto, quando nós chamamos a função atualizada, a diferença é clara e o resultado menos confuso.
1 |
power(base: 2, exponent: 3) |
Apesar de os tipos de ambas as funções sejam os mesmos, (Int, Int)-> Int
, as funções são diferentes. Em outras palavras, a segunda função não é uma redeclaração da primeira função. A sintaxe para chamar a segunda função para alguns de vocês pode lembrar Objective-C. Não só os argumentos são claramente descritos, mas a combinação de nomes de função e parâmetro descrever a finalidade da função.
Em alguns casos, você vai querer usar os mesmos nomes para os parâmetros locais e externos. Isso é possível e não há nenhuma necessidade de digitar o nome do parâmetro duas vezes. No exemplo a seguir, utilizamos o base
e o exponent
como os nomes dos parâmetros locais e externos.
1 |
func power(#base: Int, #exponent: Int) -> Int { |
2 |
var result = base |
3 |
|
4 |
for _ in 1..<exponent { |
5 |
result = result * base |
6 |
}
|
7 |
|
8 |
return result |
9 |
}
|
Iniciando o nome do parâmetro com um símbolo #
, o nome do parâmetro serve como o nome do parâmetro local e externo. Isto também significa que precisamos atualizar o corpo da função.
É importante notar que, ao fornecer um nome externo para um parâmetro, é necessário usar esse nome ao chamar a função. Isto leva-nos para valores padrão.
Valores padrão
Vimos valores de parâmetro padrão no artigo anterior, mas há um comportamento padrão importante que você precisa conhecer. Se você definir um valor padrão para um parâmetro, o Swift automaticamente atribui um nome de parâmetro externo para o parâmetro. Vejamos um exemplo do artigo anterior.
1 |
func printDate(date: NSDate, format: String = "YY/MM/dd") -> String { |
2 |
let dateFormatter = NSDateFormatter() |
3 |
dateFormatter.dateFormat = format |
4 |
return dateFormatter.stringFromDate(date) |
5 |
}
|
Como o segundo parâmetro, format
, tem um valor padrão, o Swift automaticamente define o nome do parâmetro externo da format
para format
. Em outras palavras, o resultado é o mesmo como se fossemos prefixar o format
com um símbolo #
. Você pode testar isso chamando a função em seu playground. O que acontece se você passar o format sem utilizar o nome do parêmetro externo do segundo parâmetro? A resposta está a seguir.



O Swift é bem claro sobre o que devemos fazer. Para resumir, quando você definir um parâmetro como opcional, o Swift automaticamente define o nome do parâmetro externo para o mesmo nome do parâmetro local. A idéia por trás deste comportamento é evitar confusão e ambigüidade.
Apesar deste comportamento ser uma boa prática recomendada, é possível desativá-lo. Na declaração da função atualizada abaixo, nós adicionamos um underscore onde normalmente podemos acrescentar o nome do parâmetro externo. Isso informa ao Swift que não queremos definir um nome de parâmetro externo para esse determinado parâmetro, apesar de ter um valor padrão.
1 |
func formatDate(date: NSDate, _ format: String = "YY/MM/dd") -> String { |
2 |
let dateFormatter = NSDateFormatter() |
3 |
dateFormatter.dateFormat = format |
4 |
return dateFormatter.stringFromDate(date) |
5 |
}
|
Agora podemos chamar a função formatDate
sem fornecer um nome para o segundo argumento.
1 |
formatDate(NSDate(), "dd/MM/YY") |
2. Parâmetros & Mutabilidade
Vamos revisitar o primeiro exemplo deste tutorial, a função printMessage
. O que acontece se mudarmos o valor do parâmetro message
dentro do corpo da função?
1 |
func printMessage(message: String) { |
2 |
message = "Print: \(message)" |
3 |
println(message) |
4 |
}
|
Não vai demorar muito para o Swift começar a reclamar.



Por padrão, os parâmetros de uma função são constantes. Em outras palavras, apesar de podermos acessar os valores dos parâmetros das funções, não podemos alterar seu valor. Para alterar esse comportamento padrão, adicione a palavra-chave var
ao nome do parâmetro na definição de função. O Swift irá criar uma variável cópia do valor do parâmetro para você trabalhar no corpo da função.
1 |
func printMessage(var message: String) { |
2 |
message = "Print: \(message)" |
3 |
println(message) |
4 |
}
|
Note que isto não significa que você possa passar um valor, modifica-lo na função e usar o valor modificado depois que a função finalizar seu trabalho. O Swift cria uma cópia do valor do parâmetro que existirá apenas durante o tempo de vida de função. Isso também é ilustrado no seguinte bloco de código em que podemos passar uma constante para a função de printMessage
.
1 |
func printMessage(var message: String) { |
2 |
message = "Print: \(message)" |
3 |
println(message) |
4 |
}
|
5 |
|
6 |
let myMessage = "test" |
7 |
|
8 |
printMessage(myMessage) |
3. Parâmetros variáveis
Apesar do termo parecer confuso à primeira vista, parâmetros variáveis são comuns na programação. Um parâmetro variável é um parâmetro que aceita nenhum ou muitos valores. Os valores precisam ser do mesmo tipo. Usar parâmetros variáveis no Swift é trivial, como o exemplo a seguir ilustra.
1 |
func sum(args: Int...) -> Int { |
2 |
var result = 0 |
3 |
|
4 |
for a in args { |
5 |
result += a |
6 |
}
|
7 |
|
8 |
return result |
9 |
}
|
A sintaxe é fácil de entender. Para marcar um parâmetro como variável, você acrescenta três pontos no tipo de parâmetro. No corpo da função, o parâmetro variável é acessado como um array. No exemplo acima, args
é um array de valores Int
Como o Swift precisa saber quais argumentos correspondem a quais parâmetros, um parâmetro variável precisa ser o último parâmetro. Isso também implica que uma função pode ter no máximo um parâmetro variável.
A afirmação acima também se aplica se uma função tem parâmetros com valores padrão. O parâmetro variável deve ser sempre o último parâmetro.
4. Parâmetros de entrada/saída (In-Out)
Anteriormente neste tutorial, você aprendeu como definir a mutabilidade de um parâmetro usando a palavra-chave var
. Nessa seção, enfatizei que o valor de um parâmetro só é acessível dentro do corpo da função. Se você quiser passar um valor para uma função, modificá-lo na função e devolve-lo para fora da função, parâmetros de entrada/saída são o que você está procurando.
O exemplo a seguir demonstra um exemplo de como um parâmetro de entrada/saída trabalha no Swift e como é sua sintaxe.
1 |
func prependString(inout a: String, withString b: String) { |
2 |
a = b + a |
3 |
}
|
Definimos o primeiro parâmetro como um parâmetro de entrada/saída, adicionando a palavra-chave inout
. O segundo parâmetro é um parâmetro normal com um nome externo de withString
e um nome local de b
. Vamos ver como chamamos essa função.
1 |
var world = "world" |
2 |
|
3 |
prependString(&world, withString: "Hello, ") |
Declaramos uma variável, world
, do tipo String
e passamos para a função perpendString
. O segundo parâmetro é uma string literal. Ao chamar a função, o valor da variável world
torna-se "Hello, world"
. Note que o primeiro argumento é iniciado com um "e" comercial, &
, para indicar que ele é uma parâmetro de entrada/saída.
Nem é preciso dizer que constantes e literais não podem ser passados como parâmetros entrada/saída. O Swift informará um erro quando você fizer, como ilustrado na tela abaixo.



É evidente que parâmetros de entrada/saída não podem ter valores padrão, ser variável, ou ser definidos como var
ou let
. Se você esquecer desses detalhes, gentilmente o Swift irá lembrá-lo com um erro.
5. Encadeamento (Nesting)
Em C e Objective-C, funções e métodos não podem ser encadeados. Em Swift, entretanto, encadear funções é muito comum. As funções que vimos neste e no artigo anterior são exemplos de funções globais, elas são definidas no escopo global.
Quando definimos uma função dentro de uma função global, nos referimos a essa função como uma função encadeada. Uma função encadeada tem acesso aos valores definidos em sua função envolvente. De uma olhada no exemplo a seguir para melhor entender.
1 |
func printMessage(message: String) { |
2 |
let a = "hello world" |
3 |
|
4 |
func printHelloWorld() { |
5 |
println(a) |
6 |
}
|
7 |
}
|
Apesar das funções neste exemplo não serem muito úteis, o exemplo ilustra a idéia de uma função encadeada e capturando valores. A função printHelloWorld
é apenas acessível dentro da função printMessage
. Como demonstrado no exemplo, a função printHelloWorld
tem acesso a constante a
. O valor é capturado pela função encadeada e portanto é acessível de dentro da função. O Swift se encarrega de capturar os valores, incluindo a gestão da memória desses valores.
6. Tipos de função
No artigo anterior, falamos brevemente sobre tipos de função. Uma função tem um tipo particular, composto pela tipo do parâmetro da função e pelo tipo de retorno. A função printMessage
, por exemplo, é do tipo (String) -> ()
. Lembre-se que ()
simboliza Void
, que é o equivalente a uma tupla vazia.
Por conta de todas as funções ter um tipo, é possível definir uma função que aceita outra função como parâmetro. O exemplo a seguir mostra como isso funciona.
1 |
func printMessageWithFunction(message: String, printFunction: (String) -> ()) { |
2 |
printFunction(message) |
3 |
}
|
4 |
|
5 |
let myMessage = "Hello, world!" |
6 |
|
7 |
printMessageWithFunction(myMessage, printMessage) |
A função printMessageWithFunction
aceita uma string como primeiro parâmetro e uma função do tipo (String) -> ()
como segundo parâmetro. No corpo da função, a função que passamos é chamada com o argumento message
.
O exemplo também demonstra como podemos chamar a função printMessageWithFunction
. A constante myMessage
é passada como o primeiro argumento e a função printMessage
, que definimos anteriormente, como o segundo argumento. Isso não é legal?
Como eu mencionei anteriormente, também é possível retornar uma função de uma função. O próximo exemplo é um pouco complexo, mas ilustra o que se parece com a sintaxe.
1 |
func compute(addition: Bool) -> (Int, Int) -> Int { |
2 |
func add(a: Int, b: Int) -> Int { |
3 |
return a + b |
4 |
}
|
5 |
|
6 |
func subtract(a: Int, b: Int) -> Int { |
7 |
return a - b |
8 |
}
|
9 |
|
10 |
if addition { |
11 |
return add |
12 |
} else { |
13 |
return subtract |
14 |
}
|
15 |
}
|
16 |
|
17 |
let computeFunction = compute(true) |
18 |
let result = computeFunction(1, 2) |
19 |
println(result) |
A função compute
aceita um boolean e retorna uma função do tipo (Int, Int) -> Int
. A função compute
contem duas funções encadeadas que são do tipo (Int, Int) -> Int
, add
e subtract
.
A função compute
retorna uma referência da função add
ou da subtract
, baseado no valor do parâmetro addition
. O exemplo também mostra como usar a função compute
. Armazenamos uma referência da função que é retornada pela função compute
na constante computeFunction
. Então chamamos a função armazenada em computeFunction
, passando 1
e 2
, armazenamos o resultado em result
e imprimimos o valor do result
na saída padrão. O exemplo pode parecer complexo, mas é realmente fácil de entender se você sabe o que está acontecendo.
Conclusão
Agora você deve ter uma boa compreensão de como as funções funcionam em Swift e o que pode fazer com elas. Funções é um tema comum em Swift e você as usará extensivamente ao trabalhar com Swift.
No próximo artigo, vamos mergulhar de cabeça em closures, uma poderosa construção que lembra muito os blocos em C e Objective-C, closures em JavaScript e lambdas em Ruby.
Seja o primeiro a saber sobre novas traduções–siga @tutsplus_pt no Twitter!