Routines nouvelles: existe-t-il un avertissement?
![berfis](https://cocoacafe.fr/uploads/userpics/665/n3ITV2KBRMQ8B.png)
Bonsoir,
Il m'en est arrivé une bien bonne, consécutive à ma distraction, et je me demandais si le compilateur aurait pu l'empêcher.
Je développe sous 10.9, avec comme target des machines sous 10.8. Je voyais mon application, qui semblait au point, se planter sur ces machines. Message de la console: [NSImage drawInRect] sélecteur inconnu envoyé à un objet NSImage.
Effectivement, drawInRect (sans autre paramètre) n'existe que depuis 10.9. L'éditeur me l'a donc proposé et je l'ai accepté sans vérifier...
Question: existe-t-il une option pour être averti si une routine n'est pas disponible sur la machine-cible?
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Bon, une idée comme ça: compiler avec le SDK 10.8 et voir si tout est là ?
Effectivement, il faut absolument développer avec le SDK ciblé. Le champ "Deployment Target" est juste une info dans le plist pour empêcher l'appli de se lancer si l'utilisateur n'est pas sur le bon système.
Mais attention, d'expérience ce n'est pas pour autant que tu peux te passer de testes réels sur l'OS cible. Le SDK permet de garantir les "points d'entrée" mais ce n'est pas pour autant que sous le capot 10.8 fonctionne comme 10.9. Par le passé, j'ai eu le droit à quelques bugs avec ça.
Aligator disait le contraire il y a quelques semaines. Cf http://forum.cocoacafe.fr/topic/11766-débutant-xcode-5-et-le-sdk-107/
Qui a raison ? Voulait-il dire que c'est à toi de faire attention de ne pas utiliser de nouvelles méthodes ?
Le SDK 10.7 n'est plus inclus dans Xcode 5, seulement les SDK 10.9 et 10.8.
De fait, on ne peut plus utiliser 10.7 comme Base SDK, ce qui permettrait d'être averti des API inexistantes.
Comme souligné par Ali, utiliser le 10.9 comme Base SDK est nécessaire si on veut profiter des nouvelles API de 10.9.
Non, il faut juste le récupérer d'une instal du 4.
Ba, je suis pas d'accord avec lui pour le coup. Effectivement, tu peux compiler sans soucis, sauf que après bonjour les risques de casse. Tu en as l'exemple avec ton code.
En particulier j'ai eu un débat sur le sujet sur StackOverflow où on a remarqué que dans ce cas, les valeurs de __IPHONE_OS_VERSION_MAX_ALLOWED et de __IPHONE_OS_VERSION_MIN_REQUIRED étaient faussés (c'était un test avec les SDK iOS mais bon y'a pas de raison que le problème n'arrive pas pour OSX non plus).
Or ces constantes sont un peu clé pour le Cross-SDK development, puisque beaucoup de choses se reposent sur leurs valeurs genre pour faire de la compilation ou inclusion conditionnelle...
Du coup je sais pas si la récupération d'un SDK d'un ancien Xcode pour le mettre dans un nouveau ne génère pas des risques de problèmes... enfin je peux me tromper hein, mais disons que cette constatation m'a rendu un peu sceptique de la stabilité de l'opération...
En dev Mac, jamais rencontré de problème jusqu'à présent. Tu imagines si on pouvait pas développer pour 10.7? Déjà que 10.6 représente encore 20% de part de marché aujourd'hui. Bonjour le dev à l'aveuglette...
En même temps, c'est pas choquant qu'un sdk ne connaisse pas de version supérieur à sa version d'OS. Compiler avec des define de compilation sur l'OS ça c'est moyen...
C'est pour ça que je dis que dans ce cas ça m'inspire pas confiance...
D'autant que le "SDK Compatibility Guide" d'Apple dit pourtant clairement que ce sont ces constantes / define qu'il faut tester et utiliser dans ce genre de cas...
Sous OS X, la bonne pratique veut qu'on test plutôt la version d'OS X à la volée. Cela évite ce genre de déconvenue.
Après tu fais les perfomSelector que tu veux en fonction de l'OS. La le problème c'est que le gars balance son appel de méthode direct "[_tableView setSeparatorInset:UIEdgeInsetsZero];" ce qui lui fait forcément un warning si iOS 6 le connait pas.
D'ailleurs je constate que je suis pas le seul que ça choque. Il y en a un qui préconise la même approche...
Il y a 2 cas majeurs pour le dev Cross-SDK :
- s'assurer que le code qu'on écrit compile même avec un SDK plus ancien (en encadrant de #if les portions de code qui compileront en SDK 7 mais ne pourront pas compiler en SDK 6 car les méthodes n'existaient même pas dans les headers à l'époque). Comme ça par exemple même ceux qui compilent encore avec Xcode 4 et l'ancien SDK pourront compiler le code dans erreur.
- tester au Runtime si la méthode est disponible ou non, pour que même si on a compilé avec le SDK 7, si un utilisateur lance l'appli sous iOS 6 ça n'exécute pas du code qui serait iOS7-only et n'appelle pas des méthodes qui n'existent pas sur l'OS de l'utilisateur
Ce sont 2 cas bien distincts et qu'il faut traiter tous les 2. Tout est expliqué dans le Programming Guide que j'ai mis en m'en dans l'autre post (celui cité plus haut), il explique les différents cas.
En l'occurrence les 2 cas (test à la compilation pour que ça puisse compiler sur tout SDK, et test au Runtime pour que ça puisse s'exécuter sur tout OS) sont bien distincts mais tous les 2 nécessaires et aucunement mutuellement exclusifs.
C'est le genre d'approche qui nous a valu la Browser War dans le monde Web.
C'est une très mauvaise idée de tester la version de l'OS ou le modèle de la machine. Ceux qui font ça ne suivent pas les bonnes pratiques, ni d'Apple ni du dev en général, tout langages confondus.
Ce qu'il faut c'est tester si la méthode qu'on veut appeler existe. Ou si telle fonctionnalité hardware est disponible.
On teste pas si la version actuelle est OSX.8 ou OSX.9 ni si l'iPhone est de tel modèle, on teste si telle méthode est disponible (respondsToSelector) ou si la puce GPS est dispo. C'est bien plus logique, plus évolutif et plus secure. Les macros comme celle que tu cites sont à proscrire (éventuellement à l'exception près du cas où tu veux faire un design différent pour iOs6 et iOs7... et encore en général c'est parce qu'il y a une autre logique derrière genre parce que la statusBar est translucide sur un OS et pas sur l'autre du coup autant tester ça et pas la version de l'OS...)
Mais bien sûr que c'est à proscrire au maximum. On est bien d'accord.
D'ailleurs au passage, tu te rends compte que l'exemple que tu as donné de stackoverflow est juste super mauvais? C'est le cas type où les gars vont chercher des emmerdes pour rien alors qu'avec un simple performSelector c'était réglé? ::)
De là à bannir les SDK antérieurs pour les devs, moi je trouve ça très limite. On voit d'ailleurs ce que ça donne avec le cas de Berfis. C'est pas parce qu'Apple pousse à la roue avec leurs guidelines qu'il faut se mettre à courir dedans comme un hamster. Chez Apple ils s'en moquent que ses utilisateurs vont l'accrocher au mur parce que son appli elle est bancale sur un ancien OS. Leur objectif à eux est de pousser à la migration forcée. Il n'y a d'ailleurs qu'à voir le merdier que c'est pour supporter 10.6 aujourd'hui alors qu'il représente encore plus de 20% de nos utilisateurs!
Enfin bon, c'est mon opinion après chacun fait bien comme il veut.![:D :D](https://cocoacafe.fr/resources/emoji/lol.png)
Dans le genre, j'aime beaucoup le chassé-croisé Pages 4 --> Pages 5 --> iOS 7 --> adieu iPad 1.0...
Des trucs de Borg, ça... "Toute résistance serait futile".
Je ne pense pas que les performSelector est la solutions la plus propre :
Le performSelector et ses variantes sont limitées, que ce qui se passent si on veux appeler une méthode avec plusieurs paramètres ? on sera obligé d'utiliser des fakes ( NSArray,...) c'est du code en plus, des bugs en plus.
Je ne vois pas ce que tu veux dire par la ? Les conditions de compilations avec __IPHONE_OS_VERSION_MAX_ALLOWED t consoeurs ne bannit pas les SDK antérieurs. Peut être tu parles d'autres chose ?
Non c'est méconnaitre les API Cocoa et notamment NSInvocation. Il faut tout de même relativiser la fréquence de l'usage d'exécutions conditionnelles. C'est très rare -en tout cas sous OS X-.
Et il y a des cas où effectivement l'introspection ne peut rien pour nous. J'ai par exemple le souvenir du passage de 10.5 à 10.6 et de la fonction malloc. Pour des raisons historiques liées au PPC les buffers C étaient autrefois automatiquement initialisés à 0 même sous Intel. Avec le passage à 10.6 cette commodité a disparu car elle pouvait impacter les performances sur les nouvelles architectures Intel. Donc soit on testait l'OS à la volée, soit on se retrouvait à faire un calloc systématique pour compenser (et donc à doublonner la mise à zéro sous 10.5 qui tournait bien souvent sur des machines déjà moins puissantes que celles en 10.6). Sur une petit chaine de caractère c'était pas bien méchant mais pour du traitement d'image, l'optimisation à la volée en fonction de l'OS n'était pas un luxe.
Pour être plus clair, c'était en réponse au fait de déclarer son OS cible comme par exemple 10.7 alors qu'on compile avec le SDK 10.9. Et c'est ce que préconisait Ali dans l'autre discussion. Il faut vraiment le faire en connaissance de cause. Pour lui pas de problème, il est expérimenté et connait bien les rouages. Mais pour quelqu'un de moins expert c'est très dangereux car le but premier est généralement de faire du code compatible (et beaucoup plus rarement de faire du conditionnel pour prendre en charge la nouvelle fonctionnalité tip top du dernier OS). Et c'est exactement le problème que rencontre berfis. Son appli plante sous 10.7 parce qu'il a fait usage d'un nouvel appel spécifique à 10.9 sans même s'en rendre compte. Et pendant le développement, il ne s'aperçoit de rien.