2017-05-01 1 views
1

J'ai un peu de problème de contrôle d'accès lorsque j'utilise des structures à la place des classes pour obtenir une approche de programmation plus orientée protocole.Extension de protocole: impossible d'assigner à la propriété get-only

Je reçois différents types de message sur le réseau, qui dans leur forme brute sont simplement un tableau d'octets. Donc, je commence par un protocole. Notez comment le rawBytes est marqué seulement comme{ get }afin que les appelants ne peuvent pas manipuler directement les octets bruts:

protocol NetworkDataRequest { 
    var rawBytes: [UInt8] { get } 
} 

Je suis en train d'être thread-safe et d'utiliser tout type de valeur, la bonté orientée protocole Swift, donc je crée maintenant mes différents types de message en utilisant des structures au lieu de classes, et j'adopte le protocole.

struct FileRequest: NetworkDataRequest { 
    private(set) var rawBytes: [UInt8] 
} 

struct ConnectionRequest: NetworkDataRequest { 
    private(set) var rawBytes: [UInt8] 
} 

Tous les différents types de messages partagent une structure similaire pour les 10 premiers octets, par exemple:

  • octet [0] = permissionsByte
  • byte [1] = connectionTypeByte
  • etc. ...

Puisque j'utilise des structs, je n'ai pas d'héritage. Mais j'ai encore besoin d'un moyen d'avoir un comportement similaire entre les types de message. OK, donc j'utilise une extension de protocole:

extension NetworkDataRequest { 

     var permissionsByte: UInt8 { 
      get { return bytes[0] } 
      set { bytes[0] = newValue } //<-- Nope! 
     } 

} 

Mais oups! Le rawBytes est inaccessible:

Cannot assign through subscript: 'rawBytes' is a get-only property 

Y at-il moyen de contourner cela? Sinon tous mes struct vont avoir beaucoup de code boilerplate (suce pour moi), ou je vais devoir ouvrir les rawbytes (très mauvais)

struct FileRequest: NetworkDataRequest { 

    private(set) var rawBytes: [UInt8] 

     var permissionsByte: UInt8 { 
      get { return bytes[0] } 
      set { bytes[0] = newValue } 
     } 


     var connectionTypeByte: UInt8 { 
      get { return bytes[1] } 
      set { bytes[1] = newValue } 
     } 

     ///etc... 

} 
+1

'qui, dans leur forme brute, sont simplement un tableau d'octets.» Non, ils ne le sont pas, ils sont 'Data'. Utilisez 'Data'. – Alexander

Répondre

1

Que diriez-vous quelque chose comme ça?

public protocol NetworkDataRequest { 
    var rawBytes: [UInt8] { get } 
} 

private protocol NetworkDataRequestPrivate: NetworkDataRequest { 
    var rawBytes: [UInt8] { get set } 
} 

extension NetworkDataRequest { 
    var permissionsByte: UInt8 { 
     get { return rawBytes[0] } 
    } 
} 

extension NetworkDataRequestPrivate { 
    var permissionsByte: UInt8 { 
     get { return (self as NetworkDataRequest).permissionsByte } 
     set { rawBytes[0] = newValue } 
    } 
} 

public struct FileRequest: NetworkDataRequestPrivate { 
    fileprivate(set) public var rawBytes: [UInt8] 
} 

public struct ConnectionRequest: NetworkDataRequestPrivate { 
    fileprivate(set) public var rawBytes: [UInt8] 
} 
+0

Intéressant! Le seul problème que je vois avec ceci est que maintenant je suis coincé dans un seul fichier source. Certains de ces types de requêtes sont associés à d'énormes dictionnaires occupant de nombreuses lignes de code. c'est-à-dire quand byte [9] = 100 c'est "Hello", quand byte [9] = 101 c'est "Goodbye" etc. – MH175

+0

@ MH175 Cela sera corrigé dans Swift 4. 'private' étendra l'accès aux extensions dans le même module https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md – Alexander

+0

Dans le même fichier source, pas le même module. –