Подтвердить что ты не робот

Есть ли способ для XCode предупреждать о новых вызовах API?

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

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

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

4b9b3361

Ответ 1

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

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

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

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

#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.
    }

Ответ 2

Я действительно выпустил что-то, что помогает при тестировании такого рода вещей. Это часть моего набора 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-интерфейсы я использую, которые слишком новы для цели развертывания, которую я установил.

Ответ 3

В 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, хотя.

Ответ 4

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

Ответ 5

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

Поместите следующий код вверху вашего файла ProjectName-Prefix.pch, чтобы получить предупреждение для каждого использования 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.

Ответ 6

Чтобы заставить это работать под 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__)

Ответ 7

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

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

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

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

Ответ 8

Последний 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>

Ответ 9

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

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

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

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