Pesquisa de matriz segura (verificada por limites) no Swift, através de ligações opcionais?


271

Se eu tiver uma matriz no Swift e tentar acessar um índice que está fora dos limites, há um erro de tempo de execução não surpreendente:

var str = ["Apple", "Banana", "Coconut"]

str[0] // "Apple"
str[3] // EXC_BAD_INSTRUCTION

No entanto, eu teria pensado que, com todo o encadeamento e segurança opcionais que o Swift traz, seria trivial fazer algo como:

let theIndex = 3
if let nonexistent = str[theIndex] { // Bounds check + Lookup
    print(nonexistent)
    ...do other things with nonexistent...
}

Ao invés de:

let theIndex = 3
if (theIndex < str.count) {         // Bounds check
    let nonexistent = str[theIndex] // Lookup
    print(nonexistent)   
    ...do other things with nonexistent... 
}

Mas esse não é o caso - eu tenho que usar a ifinstrução ol ' para verificar e garantir que o índice seja menor que str.count.

Tentei adicionar minha própria subscript()implementação, mas não tenho certeza de como passar a chamada para a implementação original ou acessar os itens (com base em índice) sem usar a notação subscrita:

extension Array {
    subscript(var index: Int) -> AnyObject? {
        if index >= self.count {
            NSLog("Womp!")
            return nil
        }
        return ... // What?
    }
}

2
Sei que isso é um pouco fora de moda, mas também acho que seria bom se o Swift tivesse uma sintaxe clara para realizar qualquer tipo de verificação de limites, incluindo listas. Já temos uma palavra-chave adequada para isso, em. Assim, por exemplo, se X em (1,2,7) ... ou se X em myArray
Maury Markowitz

Respostas:


652

A resposta de Alex tem bons conselhos e solução para a pergunta, no entanto, eu encontrei uma maneira mais agradável de implementar essa funcionalidade:

Swift 3.2 e mais recente

extension Collection {

    /// Returns the element at the specified index if it is within bounds, otherwise nil.
    subscript (safe index: Index) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

Swift 3.0 e 3.1

extension Collection where Indices.Iterator.Element == Index {

    /// Returns the element at the specified index if it is within bounds, otherwise nil.
    subscript (safe index: Index) -> Generator.Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

Agradecemos a Hamish por ter encontrado a solução para o Swift 3 .

Swift 2

extension CollectionType {

    /// Returns the element at the specified index if it is within bounds, otherwise nil.
    subscript (safe index: Index) -> Generator.Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

Exemplo

let array = [1, 2, 3]

for index in -20...20 {
    if let item = array[safe: index] {
        print(item)
    }
}

45
Eu acho que isso definitivamente merece atenção - bom trabalho. Gosto do safe:nome do parâmetro incluído para garantir a diferença.
Craig Otis

11
A partir do Swift 2 (Xcode 7), isso precisa de alguns ajustes:return self.indices ~= index ? self[index] : nil;
Tim

7
Em relação à versão Swift 3: possivelmente um canto-case-only-rápida, mas um aviso no entanto: há casos em que a versão índice "segura" acima não é seguro (enquanto a versão Swift 2 era): para Collectiontipos onde o Indicessão não é contíguo. Por Setexemplo, por exemplo, se acessarmos um elemento definido por index ( SetIndex<Element>), podemos executar exceções de tempo de execução para índices que são >= startIndexe < endIndex, nesse caso, o índice seguro falha (veja, por exemplo, este exemplo inventado ).
DFRI

12
AVISO! Verificar matrizes dessa maneira pode ser muito caro. O containsmétodo irá percorrer todos os índices, tornando assim um O (n). Uma maneira melhor é usar o índice e contar para verificar os limites.
Stefan Vasiljevic

6
Para evitar a geração de índices e a iteração sobre eles (O (n)), é melhor usar comparações (O (1)): os return index >= startIndex && index < endIndex ? self[index] : nil Collectiontipos possuem startIndex, endIndexquais são Comparable. Obviamente, isso não funcionará para algumas coleções estranhas que, por exemplo, não têm índices intermediários, a solução indicesé mais geral.
Zubko

57

Se você realmente deseja esse comportamento, cheira como se você quisesse um Dicionário em vez de uma Matriz. Os dicionários retornam nilao acessar as chaves ausentes, o que faz sentido, porque é muito mais difícil saber se uma chave está presente em um dicionário, pois essas chaves podem ser qualquer coisa, onde em uma matriz a chave deve estar em um intervalo de: 0a count. E é incrivelmente comum fazer iterações nesse intervalo, onde você pode ter certeza absoluta de ter um valor real em cada iteração de um loop.

Acho que a razão de não funcionar dessa maneira é uma escolha de design feita pelos desenvolvedores do Swift. Veja o seu exemplo:

var fruits: [String] = ["Apple", "Banana", "Coconut"]
var str: String = "I ate a \( fruits[0] )"

Se você já sabe que o índice existe, como na maioria dos casos em que você usa uma matriz, esse código é ótimo. No entanto, se o acesso a um índice poderia voltar nilentão você tiver alterado o tipo de retorno de Array's subscriptmétodo para ser um opcional. Isso altera seu código para:

var fruits: [String] = ["Apple", "Banana", "Coconut"]
var str: String = "I ate a \( fruits[0]! )"
//                                     ^ Added

O que significa que você precisaria desembrulhar um opcional toda vez que iterasse em uma matriz ou fizesse qualquer outra coisa com um índice conhecido, apenas porque raramente você pode acessar um índice fora dos limites. Os designers do Swift optaram por menos desembrulhar os opcionais, às custas de uma exceção de tempo de execução ao acessar índices fora dos limites. E uma falha é preferível a um erro de lógica causado por um que nilvocê não esperava em seus dados em algum lugar.

E eu concordo com eles. Portanto, você não alterará a Arrayimplementação padrão, porque você quebraria todo o código que espera valores não opcionais das matrizes.

Em vez disso, você pode subclassificar Arraye substituir subscriptpara retornar um opcional. Ou, mais praticamente, você pode estender Arraycom um método não subscrito que faz isso.

extension Array {

    // Safely lookup an index that might be out of bounds,
    // returning nil if it does not exist
    func get(index: Int) -> T? {
        if 0 <= index && index < count {
            return self[index]
        } else {
            return nil
        }
    }
}

var fruits: [String] = ["Apple", "Banana", "Coconut"]
if let fruit = fruits.get(1) {
    print("I ate a \( fruit )")
    // I ate a Banana
}

if let fruit = fruits.get(3) {
    print("I ate a \( fruit )")
    // never runs, get returned nil
}

Atualização Swift 3

func get(index: Int) ->T? precisa ser substituído por func get(index: Int) ->Element?


2
+1 (e o aceite) por mencionar o problema de alterar o tipo de retorno subscript()para um opcional - esse foi o principal obstáculo enfrentado na substituição do comportamento padrão. (Na verdade, eu não conseguia fazê-lo funcionar . ) Evitava escrever um get()método de extensão, que é a escolha óbvia em outros cenários (categorias Obj-C, alguém?), Mas get(não é muito maior do que isso [, e o torna claro que o comportamento pode diferir do que outros desenvolvedores podem esperar do operador de assinatura Swift. Obrigado!
Craig Otis

3
Para torná-lo ainda mais curto, uso em ();) Obrigado!
hyouuu

7
A partir do Swift 2.0 Tfoi renomeada para Element. Apenas um lembrete amigável :)
Stas Zhukovskiy

3
Para adicionar a essa discussão, outro motivo pelo qual a verificação de limites não é inserida no Swift para retornar um opcional é porque retornar em nilvez de causar uma exceção de um índice fora dos limites seria ambíguo. Como, por exemplo, Array<String?>também poderia retornar nulo como um membro válido da coleção, você não seria capaz de diferenciar esses dois casos. Se você tem seu próprio tipo de coleção que você sabe que nunca pode retornar um nilvalor, também conhecido como contextual ao aplicativo, você pode estender o Swift para verificação de limites seguros, conforme respondido nesta postagem.
Aaron

Funciona lindamente
kamyFC

20

Para basear-se na resposta de Nikita Kukushkin, às vezes você precisa atribuir com segurança a índices de matriz e ler a partir deles, ou seja,

myArray[safe: badIndex] = newValue

Então, aqui está uma atualização da resposta da Nikita (Swift 3.2) que também permite gravar com segurança em índices de matriz mutáveis, adicionando o nome do parâmetro safe:.

extension Collection {
    /// Returns the element at the specified index iff it is within bounds, otherwise nil.
    subscript(safe index: Index) -> Element? {
        return indices.contains(index) ? self[ index] : nil
    }
}

extension MutableCollection {
    subscript(safe index: Index) -> Element? {
        get {
            return indices.contains(index) ? self[ index] : nil
        }

        set(newValue) {
            if let newValue = newValue, indices.contains(index) {
                self[ index] = newValue
            }
        }
    }
}

2
Resposta extremamente subestimada! Esta é a maneira correta de fazer isso!
Reid

14

Válido no Swift 2

Mesmo que isso já tenha sido respondido várias vezes, eu gostaria de apresentar uma resposta mais alinhada para onde está indo a moda da programação Swift, que nas palavras de Crusty¹ é: "Pense protocol primeiro"

• O que nós queremos fazer?
- Obter um elemento de um Arraydeterminado índice somente quando for seguro; nilcaso contrário,
• Em que essa funcionalidade deve basear sua implementação?
- Array subscripting
• De onde vem esse recurso a partir de?
- Sua definição struct Arrayno Swiftmódulo possui:
• Nada mais genérico / abstrato?
- Adota o protocol CollectionTypeque garante isso também.
• Nada mais genérico / abstrato?
- Adota protocol Indexabletambém ...
• Sim, parece o melhor que podemos fazer. Podemos então estendê-lo para ter esse recurso que queremos?
- ) para trabalhar agora! Mas temos tipos muito limitados (não Int) e propriedades (nãocount
• Será o suficiente. O stdlib do Swift é feito muito bem;)

extension Indexable {
    public subscript(safe safeIndex: Index) -> _Element? {
        return safeIndex.distanceTo(endIndex) > 0 ? self[safeIndex] : nil
    }
}

Not: não é verdade, mas dá a ideia


2
Como um novato Swift, não entendo esta resposta. O que o código no final representa? Essa é uma solução e, se sim, como eu realmente a uso?
Thomas Tempelmann

3
Desculpe, esta resposta não é mais válida para o Swift 3, mas o processo certamente é. A única diferença é que agora você deve parar no Collectionprovavelmente :)
DeFrenZ

11
extension Array {
    subscript (safe index: Index) -> Element? {
        return 0 <= index && index < count ? self[index] : nil
    }
}
  • O (1) desempenho
  • tipo seguro
  • lida corretamente com os Opcionais para [MyType?] (retorna MyType ??, que pode ser desembrulhado nos dois níveis)
  • não leva a problemas para conjuntos
  • código conciso

Aqui estão alguns testes que eu executei para você:

let itms: [Int?] = [0, nil]
let a = itms[safe: 0] // 0 : Int??
a ?? 5 // 0 : Int?
let b = itms[safe: 1] // nil : Int??
b ?? 5 // nil : Int?
let c = itms[safe: 2] // nil : Int??
c ?? 5 // 5 : Int?

10
  • Como matrizes podem armazenar valores nulos, não faz sentido retornar um nulo se uma chamada de matriz [índice] estiver fora dos limites.
  • Como não sabemos como um usuário gostaria de lidar com problemas fora dos limites, não faz sentido usar operadores personalizados.
  • Por outro lado, use o fluxo de controle tradicional para desembrulhar objetos e garantir a segurança do tipo.

if let index = array.checkIndexForSafety (index: Int)

  let item = array[safeIndex: index] 

if let index = array.checkIndexForSafety (index: Int)

  array[safeIndex: safeIndex] = myObject
extension Array {

    @warn_unused_result public func checkIndexForSafety(index: Int) -> SafeIndex? {

        if indices.contains(index) {

            // wrap index number in object, so can ensure type safety
            return SafeIndex(indexNumber: index)

        } else {
            return nil
        }
    }

    subscript(index:SafeIndex) -> Element {

        get {
            return self[index.indexNumber]
        }

        set {
            self[index.indexNumber] = newValue
        }
    }

    // second version of same subscript, but with different method signature, allowing user to highlight using safe index
    subscript(safeIndex index:SafeIndex) -> Element {

        get {
            return self[index.indexNumber]
        }

        set {
            self[index.indexNumber] = newValue
        }
    }

}

public class SafeIndex {

    var indexNumber:Int

    init(indexNumber:Int){
        self.indexNumber = indexNumber
    }
}

1
Abordagem interessante. Alguma razão SafeIndexé uma classe e não uma estrutura?
stef

8

Swift 4

Uma extensão para quem prefere uma sintaxe mais tradicional:

extension Array {

    func item(at index: Int) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

você não precisa restringir os elementos da matriz a equáveis ​​para verificar se os índices contêm seu índice.
Leo Dabus

sim - bom ponto - que só seria necessário para métodos seguros adicionais como ExcluirObjeto, etc.
Matjan

5

Eu encontrei array seguro obter, definir, inserir, remover muito útil. Prefiro registrar e ignorar os erros, pois tudo o mais fica difícil de gerenciar. Código completo abaixo

/**
 Safe array get, set, insert and delete.
 All action that would cause an error are ignored.
 */
extension Array {

    /**
     Removes element at index.
     Action that would cause an error are ignored.
     */
    mutating func remove(safeAt index: Index) {
        guard index >= 0 && index < count else {
            print("Index out of bounds while deleting item at index \(index) in \(self). This action is ignored.")
            return
        }

        remove(at: index)
    }

    /**
     Inserts element at index.
     Action that would cause an error are ignored.
     */
    mutating func insert(_ element: Element, safeAt index: Index) {
        guard index >= 0 && index <= count else {
            print("Index out of bounds while inserting item at index \(index) in \(self). This action is ignored")
            return
        }

        insert(element, at: index)
    }

    /**
     Safe get set subscript.
     Action that would cause an error are ignored.
     */
    subscript (safe index: Index) -> Element? {
        get {
            return indices.contains(index) ? self[index] : nil
        }
        set {
            remove(safeAt: index)

            if let element = newValue {
                insert(element, safeAt: index)
            }
        }
    }
}

Testes

import XCTest

class SafeArrayTest: XCTestCase {
    func testRemove_Successful() {
        var array = [1, 2, 3]

        array.remove(safeAt: 1)

        XCTAssert(array == [1, 3])
    }

    func testRemove_Failure() {
        var array = [1, 2, 3]

        array.remove(safeAt: 3)

        XCTAssert(array == [1, 2, 3])
    }

    func testInsert_Successful() {
        var array = [1, 2, 3]

        array.insert(4, safeAt: 1)

        XCTAssert(array == [1, 4, 2, 3])
    }

    func testInsert_Successful_AtEnd() {
        var array = [1, 2, 3]

        array.insert(4, safeAt: 3)

        XCTAssert(array == [1, 2, 3, 4])
    }

    func testInsert_Failure() {
        var array = [1, 2, 3]

        array.insert(4, safeAt: 5)

        XCTAssert(array == [1, 2, 3])
    }

    func testGet_Successful() {
        var array = [1, 2, 3]

        let element = array[safe: 1]

        XCTAssert(element == 2)
    }

    func testGet_Failure() {
        var array = [1, 2, 3]

        let element = array[safe: 4]

        XCTAssert(element == nil)
    }

    func testSet_Successful() {
        var array = [1, 2, 3]

        array[safe: 1] = 4

        XCTAssert(array == [1, 4, 3])
    }

    func testSet_Successful_AtEnd() {
        var array = [1, 2, 3]

        array[safe: 3] = 4

        XCTAssert(array == [1, 2, 3, 4])
    }

    func testSet_Failure() {
        var array = [1, 2, 3]

        array[safe: 4] = 4

        XCTAssert(array == [1, 2, 3])
    }
}

3
extension Array {
  subscript (safe index: UInt) -> Element? {
    return Int(index) < count ? self[Int(index)] : nil
  }
}

Utilizando a extensão acima mencionada, retorne nil se a qualquer momento o índice ficar fora do limite.

let fruits = ["apple","banana"]
print("result-\(fruits[safe : 2])")

resultado - nulo


3

Sei que essa é uma pergunta antiga. Estou usando o Swift5.1 neste momento, o OP foi para o Swift 1 ou 2?

Eu precisava de algo parecido hoje, mas não queria adicionar uma extensão em escala total apenas para um local e queria algo mais funcional (mais seguro para threads?). Eu também não precisava me proteger contra índices negativos, apenas aqueles que poderiam ter passado do final de uma matriz:

let fruit = ["Apple", "Banana", "Coconut"]

let a = fruit.dropFirst(2).first // -> "Coconut"
let b = fruit.dropFirst(0).first // -> "Apple"
let c = fruit.dropFirst(10).first // -> nil

Para aqueles que discutem sobre Sequências com nada, o que você faz sobre as propriedades firste lastque retornam nada para coleções vazias?

Eu gostei disso, porque eu podia simplesmente pegar as coisas existentes e usá-las para obter o resultado desejado. Sei também que dropFirst (n) não é uma cópia inteira da coleção, apenas uma fatia. E então o comportamento já existente do primeiro assume o controle para mim.


1

Eu acho que isso não é uma boa ideia. Parece preferível criar código sólido que não resulte na tentativa de aplicar índices fora dos limites.

Considere que esse erro falha silenciosamente (conforme sugerido pelo seu código acima) retornando nil é propenso a produzir erros ainda mais complexos e intratáveis.

Você pode fazer a substituição da mesma maneira que usou e apenas escrever os subscritos à sua maneira. A única desvantagem é que o código existente não será compatível. Acho que encontrar um gancho para substituir o x [i] genérico (também sem um pré-processador de texto como em C) será um desafio.

O mais próximo que consigo pensar é

// compile error:
if theIndex < str.count && let existing = str[theIndex]

EDIT : Isso realmente funciona. One-liner !!

func ifInBounds(array: [AnyObject], idx: Int) -> AnyObject? {
    return idx < array.count ? array[idx] : nil
}

if let x: AnyObject = ifInBounds(swiftarray, 3) {
    println(x)
}
else {
    println("Out of bounds")
}

6
Eu discordo - o ponto da ligação opcional é que ela só terá êxito se a condição for atendida. (Para um opcional, significa que há um valor.) O uso de um if letnesse caso não torna o programa mais complexo nem os erros mais intratáveis. Ele simplesmente condensa a verificação tradicional de iflimites de duas instruções e a consulta real em uma instrução condensada de linha única. Há casos (particularmente que trabalham em uma UI) onde é normal para um índice para ser fora dos limites, como pedir um NSTableViewpara o selectedRowsem uma seleção.
Craig Otis

3
@Mundi, isso parece ser um comentário, e não uma resposta à pergunta do OP.
jlehr

1
@CraigOtis Não tenho certeza se concordo. Você pode escrever esta verificação de forma sucinta em uma "instrução condensada de linha única", por exemplo, usando countElementsou como o OP fez com count, mas não da maneira como a linguagem define a gravação de subscritos de matriz.
Mundi

1
@ jlehr Talvez não. É um jogo justo questionar a intenção ou a sabedoria de um problema colocado.
Mundi

2
@Mundi Heh, especialmente se você editá-lo posteriormente para realmente responder à pergunta. :-)
jlehr 15/08

1

Eu preenchi a matriz com nils no meu caso de uso:

let components = [1, 2]
var nilComponents = components.map { $0 as Int? }
nilComponents += [nil, nil, nil]

switch (nilComponents[0], nilComponents[1], nilComponents[2]) {
case (_, _, .Some(5)):
    // process last component with 5
default:
    break
}

Verifique também a extensão do subscrito com o safe:rótulo de Erica Sadun / Mike Ash: http://ericasadun.com/2015/06/01/swift-safe-array-indexing-my-favorite-thing-of-the-new-week/


0

A lista "Alterações comumente rejeitadas" para Swift contém uma menção à alteração do acesso ao subscrito da matriz para retornar um opcional em vez de travar:

Faça o Array<T>acesso ao subscrito retornar T?ou em T!vez de T: O comportamento atual da matriz é intencional , pois reflete com precisão o fato de que o acesso à matriz fora dos limites é um erro lógico. Alterar o comportamento atual reduziria o Arrayacesso a um nível inaceitável. Este tópico foi abordado várias vezes antes, mas é muito improvável que seja aceito.

https://github.com/apple/swift-evolution/blob/master/commonly_proposed.md#strings-characters-and-collection-types

Portanto, o acesso básico ao subscrito não será alterado para retornar um opcional.

No entanto, a equipe / comunidade Swift parece aberta a adicionar um novo padrão de acesso com retorno opcional às matrizes, por meio de uma função ou de um subscrito.

Isso foi proposto e discutido no fórum do Swift Evolution aqui:

https://forums.swift.org/t/add-accessor-with-bounds-check-to-array/16871

Notavelmente, Chris Lattner deu à idéia um "+1":

Concordado, a ortografia sugerida com mais frequência para isso é: yourArray[safe: idx] :, o que me parece ótimo. Sou muito +1 por adicionar isso.

https://forums.swift.org/t/add-accessor-with-bounds-check-to-array/16871/13

Portanto, isso pode ser possível fora da caixa em alguma versão futura do Swift. Eu incentivaria qualquer um que quisesse contribuir com esse segmento do Swift Evolution.


0

Para propagar por que as operações falham, os erros são melhores que os opcionais. Os subscritos não podem gerar erros, por isso deve ser um método.

public extension Collection {
  /// - Returns: same as subscript, if index is in bounds
  /// - Throws: CollectionIndexingError
  func element(at index: Index) throws -> Element {
    guard indices.contains(index)
    else { throw CollectionIndexingError() }

    return self[index]
  }
}

/// Thrown when `element(at:)` is called with an invalid index.
public struct CollectionIndexingError: Error { }
XCTAssertThrowsError( try ["🐾", "🥝"].element(at: 2) )

let optionals = [1, 2, nil]
XCTAssertEqual(try optionals.element(at: 0), 1)

XCTAssertThrowsError( try optionals.element(at: optionals.endIndex) )
{ XCTAssert($0 is CollectionIndexingError) }

0

Não sei por que ninguém colocou uma extensão que também possui um setter para aumentar automaticamente a matriz

extension Array where Element: ExpressibleByNilLiteral {
    public subscript(safe index: Int) -> Element? {
        get {
            guard index >= 0, index < endIndex else {
                return nil
            }

            return self[index]
        }

        set(newValue) {
            if index >= endIndex {
                self.append(contentsOf: Array(repeating: nil, count: index - endIndex + 1))
            }

            self[index] = newValue ?? nil
        }
    }
}

O uso é fácil e funciona a partir do Swift 5.1

var arr:[String?] = ["A","B","C"]

print(arr) // Output: [Optional("A"), Optional("B"), Optional("C")]

arr[safe:10] = "Z"

print(arr) // [Optional("A"), Optional("B"), Optional("C"), nil, nil, nil, nil, nil, nil, nil, Optional("Z")]

Nota: Você deve entender o custo de desempenho (tanto em tempo quanto em espaço) ao aumentar rapidamente uma matriz - mas para pequenos problemas, às vezes, você só precisa que o Swift pare de se movimentar no pé


-1

Eu fiz uma extensão simples para array

extension Array where Iterator.Element : AnyObject {
    func iof (_ i : Int ) -> Iterator.Element? {
        if self.count > i {
            return self[i] as Iterator.Element
        }
        else {
            return nil
        }
    }

}

funciona perfeitamente como projetado

Exemplo

   if let firstElemntToLoad = roots.iof(0)?.children?.iof(0)?.cNode, 

-1

Uso do Swift 5

extension WKNavigationType {
    var name : String {
        get {
            let names = ["linkAct","formSubm","backForw","reload","formRelo"]
            return names.indices.contains(self.rawValue) ? names[self.rawValue] : "other"
        }
    }
}

acabou com, mas realmente queria fazer geralmente como

[<collection>][<index>] ?? <default>

mas como a coleção é contextual, acho que é adequada.


Como essa resposta é diferente da resposta aceita? Quanto a mim, parece exatamente o mesmo (duplicado).
Legonaftik

-1

Quando você só precisa obter valores de uma matriz e não se importa com uma pequena penalidade de desempenho (por exemplo, se sua coleção não for grande), há uma alternativa baseada em dicionário que não envolve (muito genérica, para meu extensão) coleção:

// Assuming you have a collection named array:
let safeArray = Dictionary(uniqueKeysWithValues: zip(0..., array))
let value = safeArray[index] ?? defaultValue;
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.