Есть ли способ для Xcode предупредить о новых вызовах API? - PullRequest
25 голосов
/ 13 января 2011

Я неоднократно видел сбойные ошибки в iOS 3.x из-за использования нового вызова, который был введен в 4.x без надлежащей проверки.

Есть ли способ для Xcodeпредупредить о классах, методах и процедурах, которые доступны только в более поздней версии, чем цель развертывания?

Таким образом, я мог бы легко просмотреть весь код и убедиться, что он правильно обусловлен.

Ответы [ 9 ]

27 голосов
/ 19 января 2012

Я действительно выпустил что-то, что помогает с тестированием такого рода вещей. Это часть моего набора классов MJGFoundation с именем MJGAvailability.h .

Я использовал его, применив его в своем файле PCH, например:

#define __IPHONE_OS_VERSION_SOFT_MAX_REQUIRED __IPHONE_4_0
#import "MJGAvailability.h"

// The rest of your prefix header as normal
#import <UIKit/UIKit.h>

Затем он предупредит (возможно, со странным предупреждением об устаревании) об используемых API, которые являются слишком новыми для цели, которую вы установили как «мягкий максимум» согласно #define __IPHONE_OS_VERSION_SOFT_MAX_REQUIRED. Также, если вы не определили __IPHONE_OS_VERSION_SOFT_MAX_REQUIRED, то по умолчанию это будет ваша цель развертывания.

Я считаю это полезным, потому что тогда я могу дважды проверить, какие API, которые я использую, являются слишком новыми для цели развертывания, которую я установил.

15 голосов
/ 13 июля 2017

Если вы используете XCode7.3 и выше, вы можете установить другой флаг предупреждения: -Wpartial-availability, тогда xcode покажет предупреждение для API, более раннего, чем целевая версия развертывания. enter image description here

15 голосов
/ 10 мая 2016

По крайней мере, в OS X, с недавним clang / SDK, теперь есть опция -Wpartial-availability (добавьте ее, например, в «другие опции предупреждения») Затем можно определить следующие макросы для инкапсуляции кода, который обрабатывает тестирование во время выполнения, если метод поддерживается

#define START_IGNORE_PARTIAL _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wpartial-availability\"")
#define END_IGNORE_PARTIAL _Pragma("clang diagnostic pop")

Хотя я не тестировал на iOS.

12 голосов
/ 24 января 2012

После просмотра AvailabilityInternal.h я понял, что все доступные версии выше цели развертывания помечены макросом __AVAILABILITY_INTERNAL_WEAK_IMPORT.

Поэтому я могу генерировать предупреждения, переопределивmacro:

#import <Availability.h>
#undef  __AVAILABILITY_INTERNAL_WEAK_IMPORT
#define __AVAILABILITY_INTERNAL_WEAK_IMPORT \
    __attribute__((weak_import,deprecated("API newer than Deployment Target.")))

Помещая этот код в предварительно скомпилированный заголовок проекта, любое использование API, которое может вызвать сбой в самой низкой поддерживаемой версии iOS, теперь генерирует предупреждение.Если вы правильно защитите вызов, вы можете отключить предупреждение специально для этого вызова (измененный пример из Руководства по совместимости SDK от Apple *1011*):

#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
    if ([UIPrintInteractionController class]) {
        // Create an instance of the class and use it.
    }
#pragma GCC diagnostic warning "-Wdeprecated-declarations"
    else {
        // Alternate code path to follow when the
        // class is not available.
    }
4 голосов
/ 31 октября 2013

Чтобы заставить это работать в XCode 5, вам также необходимо переопределить макросы NS_AVAILABLE и NS_DEPRECATED, поскольку CFAvailability.h различает компиляторы, которые поддерживают функцию attribute_availability_with_message. Скопируйте приведенный выше импорт «MJGAvailability.h» в предварительно скомпилированный заголовок, чтобы заставить его работать с новым компилятором Apple LLVM:

#import <Availability.h>
#import <Foundation/NSObjCRuntime.h>

#undef CF_AVAILABLE
#undef CF_AVAILABLE_MAC
#undef CF_AVAILABLE_IOS
#undef CF_DEPRECATED
#undef CF_DEPRECATED_MAC
#undef CF_DEPRECATED_IOS
#undef CF_ENUM_AVAILABLE
#undef CF_ENUM_AVAILABLE_MAC
#undef CF_ENUM_AVAILABLE_IOS
#undef CF_ENUM_DEPRECATED
#undef CF_ENUM_DEPRECATED_MAC
#undef CF_ENUM_DEPRECATED_IOS

#undef NS_AVAILABLE
#undef NS_AVAILABLE_MAC
#undef NS_AVAILABLE_IOS
#undef NS_DEPRECATED
#undef NS_DEPRECATED_MAC
#undef NS_DEPRECATED_IOS
#undef NS_ENUM_AVAILABLE
#undef NS_ENUM_AVAILABLE_MAC
#undef NS_ENUM_AVAILABLE_IOS
#undef NS_ENUM_DEPRECATED
#undef NS_ENUM_DEPRECATED_MAC
#undef NS_ENUM_DEPRECATED_IOS
#undef NS_AVAILABLE_IPHONE
#undef NS_DEPRECATED_IPHONE

#undef NS_CLASS_AVAILABLE
#undef NS_CLASS_DEPRECATED
#undef NS_CLASS_AVAILABLE_IOS
#undef NS_CLASS_AVAILABLE_MAC
#undef NS_CLASS_DEPRECATED_MAC
#undef NS_CLASS_DEPRECATED_IOS

//CF macros redefinition
#define CF_AVAILABLE(_mac, _ios) __OSX_AVAILABLE_STARTING(__MAC_##_mac, __IPHONE_##_ios)
#define CF_AVAILABLE_MAC(_mac) __OSX_AVAILABLE_STARTING(__MAC_##_mac, __IPHONE_NA)
#define CF_AVAILABLE_IOS(_ios) __OSX_AVAILABLE_STARTING(__MAC_NA, __IPHONE_##_ios)

#define CF_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, ...) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_##_macIntro, __MAC_##_macDep, __IPHONE_##_iosIntro, __IPHONE_##_iosDep)
#define CF_DEPRECATED_MAC(_macIntro, _macDep, ...) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_##_macIntro, __MAC_##_macDep, __IPHONE_NA, __IPHONE_NA)
#define CF_DEPRECATED_IOS(_iosIntro, _iosDep, ...) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_NA, __MAC_NA, __IPHONE_##_iosIntro, __IPHONE_##_iosDep)

#define CF_ENUM_AVAILABLE(_mac, _ios) CF_AVAILABLE(_mac, _ios)
#define CF_ENUM_AVAILABLE_MAC(_mac) CF_AVAILABLE_MAC(_mac)
#define CF_ENUM_AVAILABLE_IOS(_ios) CF_AVAILABLE_IOS(_ios)

#define CF_ENUM_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, ...) CF_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, __VA_ARGS__)
#define CF_ENUM_DEPRECATED_MAC(_macIntro, _macDep, ...) CF_DEPRECATED_MAC(_macIntro, _macDep, __VA_ARGS__)
#define CF_ENUM_DEPRECATED_IOS(_iosIntro, _iosDep, ...) CF_DEPRECATED_IOS(_iosIntro, _iosDep, __VA_ARGS__)

//NS macros redefinition
#define NS_AVAILABLE(_mac, _ios) CF_AVAILABLE(_mac, _ios)
#define NS_AVAILABLE_MAC(_mac) CF_AVAILABLE_MAC(_mac)
#define NS_AVAILABLE_IOS(_ios) CF_AVAILABLE_IOS(_ios)

#define NS_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, ...) CF_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, __VA_ARGS__)
#define NS_DEPRECATED_MAC(_macIntro, _macDep, ...) CF_DEPRECATED_MAC(_macIntro, _macDep, __VA_ARGS__)
#define NS_DEPRECATED_IOS(_iosIntro, _iosDep, ...) CF_DEPRECATED_IOS(_iosIntro, _iosDep, __VA_ARGS__)

#define NS_ENUM_AVAILABLE(_mac, _ios) CF_ENUM_AVAILABLE(_mac, _ios)
#define NS_ENUM_AVAILABLE_MAC(_mac) CF_ENUM_AVAILABLE_MAC(_mac)
#define NS_ENUM_AVAILABLE_IOS(_ios) CF_ENUM_AVAILABLE_IOS(_ios)

#define NS_ENUM_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, ...) CF_ENUM_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, __VA_ARGS__)
#define NS_ENUM_DEPRECATED_MAC(_macIntro, _macDep, ...) CF_ENUM_DEPRECATED_MAC(_macIntro, _macDep, __VA_ARGS__)
#define NS_ENUM_DEPRECATED_IOS(_iosIntro, _iosDep, ...) CF_ENUM_DEPRECATED_IOS(_iosIntro, _iosDep, __VA_ARGS__)

#define NS_AVAILABLE_IPHONE(_ios) CF_AVAILABLE_IOS(_ios)
#define NS_DEPRECATED_IPHONE(_iosIntro, _iosDep) CF_DEPRECATED_IOS(_iosIntro, _iosDep)

#define NS_CLASS_AVAILABLE(_mac, _ios) __attribute__((visibility("default"))) NS_AVAILABLE(_mac, _ios)
#define NS_CLASS_DEPRECATED(_mac, _macDep, _ios, _iosDep, ...) __attribute__((visibility("default"))) NS_DEPRECATED(_mac, _macDep, _ios, _iosDep, __VA_ARGS__)

#define NS_CLASS_AVAILABLE_IOS(_ios) NS_CLASS_AVAILABLE(NA, _ios)
#define NS_CLASS_AVAILABLE_MAC(_mac) NS_CLASS_AVAILABLE(_mac, NA)
#define NS_CLASS_DEPRECATED_MAC(_macIntro, _macDep, ...) NS_CLASS_DEPRECATED(_macIntro, _macDep, NA, NA, __VA_ARGS__)
#define NS_CLASS_DEPRECATED_IOS(_iosIntro, _iosDep, ...) NS_CLASS_DEPRECATED(NA, NA, _iosIntro, _iosDep, __VA_ARGS__)
4 голосов
/ 01 июня 2012

Это основано на ответе Ben S , но включает поддержку GCC и LLVM-GCC.Атрибут deprecated GCC не принимает аргумент сообщения , как у clang, поэтому передача его приводит к ошибке компилятора практически в каждом файле.* файл для получения предупреждения при каждом использовании API, который может быть недоступен во всех ваших целевых версиях:

#import <Availability.h>
#undef  __AVAILABILITY_INTERNAL_WEAK_IMPORT
#ifdef __clang__
#define __AVAILABILITY_INTERNAL_WEAK_IMPORT \
__attribute__((weak_import,deprecated("API newer than Deployment Target.")))
#else
#define __AVAILABILITY_INTERNAL_WEAK_IMPORT \
__attribute__((weak_import,deprecated))
#endif

Как говорит Бен, если вы намеренно делаете это (возможно, проверяя селекторво время выполнения) вы можете скрыть предупреждение с помощью этой конструкции:

#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
    - (void)conditionallyUseSomeAPI {
        // Check for and use the appropriate API for this iOS version
    }
#pragma GCC diagnostic warning "-Wdeprecated-declarations"

К сожалению, вы не можете сделать это внутри функции, по крайней мере, в i686-apple-darwin10-llvm-gcc-4.2 (GCC) 4.2.1.

0 голосов
/ 05 августа 2016

Последний Xcode не работал с другими ответами.Это работает для меня (только ищет проблемы UIKit).

Причина в том, что более новые версии Clang имеют встроенный атрибут доступности.

#define TESTING_COMPILATION_TARGET
// only enable when trying to diagnose what APIs are being inappropriately used
#ifdef TESTING_COMPILATION_TARGET
#import <Availability.h>

#define __MYUNSUPPORTED __attribute((deprecated("API version unsupported")))

#define __MYUNSUPPORTED_IOS_NA __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_2_0
#define __MYUNSUPPORTED_IOS_2_1
#define __MYUNSUPPORTED_IOS_2_2
#define __MYUNSUPPORTED_IOS_3_0
#define __MYUNSUPPORTED_IOS_3_1
#define __MYUNSUPPORTED_IOS_3_2
#define __MYUNSUPPORTED_IOS_4_0
#define __MYUNSUPPORTED_IOS_4_1
#define __MYUNSUPPORTED_IOS_4_2
#define __MYUNSUPPORTED_IOS_4_3
#define __MYUNSUPPORTED_IOS_5_0
#define __MYUNSUPPORTED_IOS_5_1
#define __MYUNSUPPORTED_IOS_6_0
#define __MYUNSUPPORTED_IOS_6_1
#define __MYUNSUPPORTED_IOS_7_0
#define __MYUNSUPPORTED_IOS_7_1 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_8_0 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_8_1 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_8_2 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_8_3 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_8_4 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_9_0 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_9_1 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_9_2 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_9_3 __MYUNSUPPORTED

#import <Foundation/Foundation.h>

#undef CF_AVAILABLE
#define CF_AVAILABLE(_mac, _ios) __MYUNSUPPORTED_IOS_##_ios

#undef NS_AVAILABLE
#define NS_AVAILABLE(_mac, _ios) __MYUNSUPPORTED_IOS_##_ios

#undef CF_AVAILABLE_IOS
#define CF_AVAILABLE_IOS(_ios) __MYUNSUPPORTED_IOS_##_ios

#undef NS_AVAILABLE_IOS
#define NS_AVAILABLE_IOS(_ios) __MYUNSUPPORTED_IOS_##_ios

#endif // testing

#import <UIKit/UIKit.h>
0 голосов
/ 13 января 2011

это не интегрировано в набор инструментов. Один из способов проверить это - просто создать проверку во время выполнения, которая будет утверждаться (во время разработки при запуске в более новых версиях ОС).

assert([<CLASS> instancesRespondToSelector:@selector(potato)]);

, затем просто добавьте это к одной из процедур инициализации вашей библиотеки.

Вы также можете создать сценарий, который будет подсчитывать количество предупреждений, выданных для конкретного перевода - если количество предупреждений изменяется, то у вас есть обновления, которые нужно сделать.

0 голосов
/ 13 января 2011

Нет, такого предупреждения нет. Однако, когда вы используете новый API (поскольку вы, очевидно, пишете его позже), просто проверьте документы, когда они были доступны.

Кроме того, если вы поддерживаете 3.0 и используете новый SDK для разработки, вы обязательно должны проводить тестирование на реальных устройствах, работающих под управлением 3.0

Еще одна вещь, которую вы могли бы сделать, - написать свою собственную утилиту, которая анализирует макросы доступности в заголовках и затем предупреждает вас, если вы вызываете что-то, чего не должно быть.

Однако я должен повторить: если вы ориентируетесь на более старую версию и используете более новый SDK, вы должны проверить документы, чтобы узнать, когда API стал доступен, и протестировать соответствующим образом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...