[Swift]Conversion AnyObject? => UInt64

DrakenDraken Membre
février 2016 modifié dans Objective-C, Swift, C, C++ #1

J'ai voulu récupérer une valeur stockée dans un AnyObject? et la convertir en UInt64, de cette manière (Swift 2.1 - Xcode 7.2.1) :



let test:AnyObject? = 1234
print ("Test : ", test)

if let valeur = test as? UInt64 {
print ("valeur : ", valeur)
} else {
print ("Pas de valeur !")
}


Cela ne fonctionne pas :



 


Test :  Optional(1234)


Pas de valeur ..


 


 


J'ai dû passer par une syntaxe plus "rustique" pour y parvenir :



let test:AnyObject? = 1234
print ("Test : ", test)

if test != nil {
let v:UInt64 = test!.unsignedLongLongValue
print ("v : ", v)
}



 


Test :  Optional(1234)


v :  1234


 


 


Quelqu'un sait-il pourquoi mon premier code est erroné ?

«1

Réponses

  • Après test, cela fonctionne avec UInit, mais pas UInt64 ou UInt32.



    let test:AnyObject? = 1234
    print ("Test : ", test)

    if let valeur = test as? UInt {
    print ("valeur : ", valeur)
    } else {
    print ("Pas de valeur !")
    }



     


    Test :  Optional(1234)


    valeur :  1234


     

  • DrakenDraken Membre
    février 2016 modifié #3

    Après d'autres tests, on peut convertir un AnyObject? en Int ou UInt avec la syntaxe let var = var as? quelque chose


    Mais ne fonctionne pas avec Int32, Int64, UInt32, UInt64. Ou quelque chose m'échappe ..



    let test:AnyObject? = 1234
    print ("Test : ", test)

    if let valeur = test as? UInt {
    print ("UInt: ", valeur)
    } else {
    print ("Pas de valeur UInt ..")
    }

    if let valeur = test as? UInt32 {
    print ("UInt 32: ", valeur)
    } else {
    print ("Pas de valeur UInt32 ..")
    }

    if let valeur = test as? UInt64 {
    print ("UInt64: ", valeur)
    } else {
    print ("Pas de valeur UInt64 ..")
    }

    if let valeur = test as? Int {
    print ("Int: ", valeur)
    } else {
    print ("Pas de valeur Int ..")
    }

    if let valeur = test as? Int32 {
    print ("Int32: ", valeur)
    } else {
    print ("Pas de valeur Int32 ..")
    }

    if let valeur = test as? Int64 {
    print ("Int64: ", valeur)
    } else {
    print ("Pas de valeur Int64 ..")
    }



     


    Test :  Optional(1234)


    UInt:  1234


    Pas de valeur UInt32 ..


    Pas de valeur UInt64 ..


    Int:  1234


    Pas de valeur Int32 ..


    Pas de valeur Int64 ..


     


     


    J'ai pourtant vu des exemples de codes sur StackOverFlow convertissant des AnyObject? en UInt64. Soit les types ne testent pas les sources qu'ils tapent, soit il y a eu un changement dans Xcode.


  • DrakenDraken Membre
    février 2016 modifié #4

    A noter que l'utilisation de l'opérateur as! fonctionne très bien avec un AnyObject? => UInt



    let test:AnyObject? = 1234
    print ("Test : ", test)

    let vvv = test as! UInt
    print ("vvv : ",vvv)


    Et fait carrément planter l'application avec un UInt64 (normal, puisque le casting d'un AnyObject? en UInt64 retourne un nil).



    let test:AnyObject? = 1234
    print ("Test : ", test)

    let vvv = test as! UInt64
    print ("vvv : ",vvv)


  • samirsamir Membre
    février 2016 modifié #5

    Salut,


     


    Tous ces types sont "bridged" automatiquement vers NSNumber : Int, UInt, Float, Double, Bool. 


     


    Par exemple on peut faire comme ça :



    let a : NSNumber = 42
    //ou bien
    let b : NSNumber = Int(42)

    Par contre cette ligne ne compile pas, par ce que UInt64 n'est pas "bridged" vers NSNumber:



    let c : NSNumber = UInt64(1)

    Donc to problème vient de la ( le fait que les types Int64, ... ne sont pas convertie automatiquement vers NSNUmber).


     


     


    Ton code :



    let test:AnyObject? = 1234

    Cette ligne ne crée pas un Int pour le mettre dans AnyObject mais plutôt un NSNumber. C'est pour ça que la reconversion inverse ne marche pas. (ce que j'ai dit en haut)


     


    Pourquoi Int64 par exemple ne supporte pas la convention automatique vers NSNumber ? Je ne sais pas vraiment :), mais je pense qu'ils veulent  mettre moins de conversions automatiques. 


     


     


    https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6-XID_42


  • Un article intéressant sur le sujet (en anglais mais court) : http://blog.scottlogic.com/2014/09/24/swift-anyobject.html.


     


    Je suis bien content d'avoir commencé swift sur Linux car, comme Foundation n'est pas encore complètement disponible, donc j'utilise principalement des types natifs de swift.


     


    Or quand on utilise Foundation, on utilise parfois des types de Foundation sans vraiment s'en rendre compte, même si on n'utilise pas AnyObject.


     


    Ce n'est pas très grave en soit, mais je pense que c'est bien de comprendre ce qui vient de Swift et ce qui vient de Foundation.




  • Donc to problème vient de la ( le fait que les types Int64, ... ne sont pas convertie automatiquement vers NSNUmber).


     


    Ton code :



    let test:AnyObject? = 1234

    Cette ligne ne crée pas un Int pour le mettre dans AnyObject mais plutôt un NSNumber. C'est pour ça que la reconversion inverse ne marche pas. (ce que j'ai dit en haut)


     


    Pourquoi Int64 par exemple ne supporte pas la convention automatique vers NSNumber ? Je ne sais pas vraiment :), mais je pense qu'ils veulent  mettre moins de conversions automatiques. 


     




    Merci de l'explication. A l'origine, je ne créais pas le AnyObject?. Je me suis inspiré d'un exemple de source Swift de SOF pour comprendre comment lire la taille d'un fichier disque en lisant son attribut NSFileSize (un AnyObject?). L'auteur le caste en UInt64, alors j'ai fait la même chose. Je présume qu'il n'a pas testé son code avant de le publier.



  • Je suis bien content d'avoir commencé swift sur Linux




    Sors de ce corps, Satan !


    Le 10 Août c'est la fin pour toi .. (sortie de SOS Fantômes III au cinéma)

  • Joanna CarterJoanna Carter Membre, Modérateur
    février 2016 modifié #9
    Bonsoir Draken


    Ni UInt, ni UInt32, ni UInt64, ni Int, ni Int32, ni Int64 peuvent être trouvés dans un AnyObject (sauf dans un NSNumber) parce qu'ils sont tous les structs. On peut, cependant, les tenir dans un Any.


    A l'origine, je ne créais pas le AnyObject?. Je me suis inspiré d'un exemple de source Swift de SOF pour comprendre comment lire la taille d'un fichier disque en lisant son attribut NSFileSize (un AnyObject?). L'auteur le caste en UInt64, alors j'ai fait la même chose. Je présume qu'il n'a pas testé son code avant de le publier.


    Si tu lisais les docs sur l'attribut NSFileSize, tu y trouverais que la valeur tenue est un String, pas un NSNumber.


    Du coup, pour la convertir, il faudrait faire plus qu'un cast. Je suis d'accord avec toi que l'auteur avait tort.
  • DrakenDraken Membre
    février 2016 modifié #10

    Oki, merci nounours !


     


    Au final j'ai fais comme ça :



    func tailleFichier(fileName:String) -> UInt64 {
    let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
    let fileURL = documentsURL.URLByAppendingPathComponent(fileName)
    let fileAttr = try! NSFileManager.defaultManager().attributesOfItemAtPath(fileURL.path!)
    let size = fileAttr[NSFileSize]?.unsignedLongLongValue
    if let size = size {
    return size
    }
    return 0
    }



    EDIT : En relisant le code, je me rend compte que cela vas planter en testant la taille d'un fichier inexistant. Nouvelle version, avec un try? pour obtenir un fileAttr optional et éviter le crash.



    func tailleFichier(fileName:String) -> UInt64 {
    let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
    let fileURL = documentsURL.URLByAppendingPathComponent(fileName)
    let fileAttr = try? NSFileManager.defaultManager().attributesOfItemAtPath(fileURL.path!)
    if let fileAttr = fileAttr {
    let size = fileAttr[NSFileSize]?.unsignedLongLongValue
    if let size = size {
    return size
    }
    }
    return 0
    }



  • Le mieux c'est de faire une fonction qui te propage une erreur en cas d'échec et non pas retourner 0.


     


    Si la taille de ton fichier est égale 0 ( possible ?), tu considères que c'est ton URL qui n'est pas bonne, ... ?


    Aussi, ne pas forcer tes optionnels (path!), il vaut mieux les "binder" ou utiliser un guard. 


  • Joanna CarterJoanna Carter Membre, Modérateur
    février 2016 modifié #12

    Après quelques expériences :



    // exemple d'erreur pour le throw
    enum TailleFichierError : ErrorType
    {
    case FileNotFound
    case AttributeNotFound
    }

    func tailleFichier(fileName:String) throws -> UInt64
    {
    let fileManager = NSFileManager.defaultManager()

    // garanti à  réussir
    let documentsURL = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!

    let fileURL = documentsURL.URLByAppendingPathComponent(fileName)

    // possible que l'URL soit mal écrit
    guard fileManager.fileExistsAtPath(fileURL.path!) else
    {
    throw TailleFichierError.FileNotFound
    }

    do
    {
    // attributesOfItemAtPath throws. Du coup, il faut l'emballer dans un do..catch
    let fileAttr: NSDictionary = try fileManager.attributesOfItemAtPath(fileURL.path!)

    return fileAttr.fileSize() // extension sur NSDictionary !!!
    }
    catch _ as NSError
    {
    throw TailleFichierError.AttributeNotFound
    }
    }

    // test code
    do
    {
    try tailleFichier("à‰pavesPanoramique.jpg")
    }
    catch TailleFichierError.FileNotFound
    {
    print("Fichier non trouvé")
    }
    catch TailleFichierError.AttributeNotFound
    {
    print("Attribut non trouvé")
    }

  • AliGatorAliGator Membre, Modérateur
    Pauvres poneys...
  • C'est utile les poneys. On fait de la colle avec les cadavres ..

  • Joanna CarterJoanna Carter Membre, Modérateur

    Pauvres poneys...




    Tu pourrais m'éclaircir ?

    C'est utile les poneys. On fait de la colle avec les cadavres ..




    :)
  • samirsamir Membre
    février 2016 modifié #16
  • DrakenDraken Membre
    février 2016 modifié #17


    Tu pourrais m'éclaircir ?

     




    Je pense que cela m'était destiné. Traduction : "Arrête de tuer des poneys et vas lire mon blog Crunchy Développement". 


  • Joanna CarterJoanna Carter Membre, Modérateur
    Je les ai déjà  lu, je croyais que je les ai compris. à‰videmment, j'ai tort.


  • Pauvres poneys...




    C'est triste, à  une époque on avait droit à  une page d'explications compléte. Maintenant juste une référence sibylline que seuls les gens parlant le Ali peuvent comprendre.

  • MODERATION : Une bonne âme peut-elle séparer ce topic en deux parties ?


     


    - [Swift] Conversion AnyObject? => UInt64


    - Boucherie chevaline [Swift] Lecture de la taille d'un fichier avec gestion des erreurs

  • Joanna CarterJoanna Carter Membre, Modérateur

    OK. Ayant relu ton article Ali, je voudrais te demander ou se trouvent ces fameux poneys ?



    let documentsURL = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!

    Ce, c'est garanti à  réussir.



    let fileURL = documentsURL.URLByAppendingPathComponent(fileName)

    guard fileManager.fileExistsAtPath(fileURL.path!) else
    {
    throw TailleFichierError.FileNotFound
    }

    Ici, fileURL.path est simplement les composants de l'URL en String. ça peut échouer ?



    do
    {
    // attributesOfItemAtPath throws. Du coup, il faut l'emballer dans un do..catch
    let fileAttr: NSDictionary = try fileManager.attributesOfItemAtPath(fileURL.path!)

    return fileAttr.fileSize() // extension sur NSDictionary !!!
    }
    catch _ as NSError
    {
    throw TailleFichierError.AttributeNotFound
    }

    Ici, j'utilise un do...catch pour attraper des soucis avec attributesOfItemAtPath; et fileURL.path est comme ci-dessus.


     


    Je rate quelque chose d'autre ?


  • Petite variante de ma méthode, plus dans l'esprit Swift, retournant un Uint64?, afin d'avoir un nil en cas d'erreur. Ce n'est pas aussi complet que le code de Joanna, mais c'est toujours mieux qu'avant. 



    func fileSize(fileName:String) -> UInt64? {
    let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
    let fileURL = documentsURL.URLByAppendingPathComponent(fileName)
    let fileAttr = try? NSFileManager.defaultManager().attributesOfItemAtPath(fileURL.path!)
    if let fileAttr = fileAttr {
    let size = fileAttr[NSFileSize]?.unsignedLongLongValue
    if let size = size {
    return size
    }
    }
    return nil
    }
     
  • Joanna CarterJoanna Carter Membre, Modérateur
    février 2016 modifié #24

    Est-ce que tu sais que tu puisses écrire encore moins de code que ça ?



    func fileSize(fileName:String) -> UInt64?
    {
    let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!

    let fileURL = documentsURL.URLByAppendingPathComponent(fileName)

    if let fileAttr = try? NSFileManager.defaultManager().attributesOfItemAtPath(fileURL.path!),
    size = fileAttr[NSFileSize]?.unsignedLongLongValue
    {
    return size
    }

    return nil
    }

  • Joanna CarterJoanna Carter Membre, Modérateur
    février 2016 modifié #25

    Ou, encore moins que ça :



    func fileSize(fileName:String) -> UInt64?
    {
    if let documentsURL: NSURL? = NSFileManager.defaultManager().URLsForDirectory(.PicturesDirectory, inDomains: .UserDomainMask).first,
    fileURL = documentsURL?.URLByAppendingPathComponent(fileName),
    fileAttr = try? NSFileManager.defaultManager().attributesOfItemAtPath(fileURL.path!)
    {
    return fileAttr[NSFileSize]?.unsignedLongLongValue
    }

    return nil
    }

  • DrakenDraken Membre
    février 2016 modifié #26

    J'aime le minimalisme .. tant que cela ne nuit pas à  la lisibilité du code. Là  c'est parfait. Merci Nounours !


     


    EDIT : Pourquoi ta première version cherche le fichier dans .DocumentDirectory et la seconde dans .PicturesDirectory ?

  • colas_colas_ Membre
    février 2016 modifié #27

    Quid de



    func fileSize(fileName:String) -> UInt64?
    {
    if let documentsURL: NSURL? = NSFileManager.defaultManager().URLsForDirectory(.PicturesDirectory, inDomains: .UserDomainMask).first,
    fileURL = documentsURL?.URLByAppendingPathComponent(fileName),
    filePath = fileURL.path?,
    fileAttr = try? NSFileManager.defaultManager().attributesOfItemAtPath(filePath)
    {
    return fileAttr[NSFileSize]?.unsignedLongLongValue
    }

    return nil
    }

  • DrakenDraken Membre
    février 2016 modifié #28

    C'est .. illisible !


    T'as oublié les balises de code du forum ?


     


    EDIT : c'est mieux comme ça.

  • AliGatorAliGator Membre, Modérateur
    février 2016 modifié #29
    func fileSize(fileName:String) -> UInt64?
    {
    guard let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first,
    filePath = documentsURL.URLByAppendingPathComponent(fileName).path,
    fileAttr = try? NSFileManager.defaultManager().attributesOfItemAtPath(filePath)
    else { return nil }
    return fileAttr[NSFileSize]?.unsignedLongLongValue
    }
    (Bon pas testé j'ai juste copié les codes plus haut et réarrangé, il peut y avoir des erreurs de syntaxe)
  • L'instruction guard est bien sympathique.

  • Joanna CarterJoanna Carter Membre, Modérateur



    func fileSize(fileName:String) -> UInt64?
    {
    guard let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first,
    filePath = documentsURL.URLByAppendingPathComponent(fileName).path,
    fileAttr = try? NSFileManager.defaultManager().attributesOfItemAtPath(filePath)
    else { return nil }
    return fileAttr[NSFileSize]?.unsignedLongLongValue
    }

    (Bon pas testé j'ai juste copié les codes plus haut et réarrangé, il peut y avoir des erreurs de syntaxe)

     




     


    Quand même ça marche bien 

Connectez-vous ou Inscrivez-vous pour répondre.