Clang обнуляемость предупреждения и как к ним подойти
Недавно я включил CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION
в XCode, и я перегружен предупреждениями, связанными с обнуляемостью в моем коде Objective-C. Один тип предупреждения, который является наиболее распространенным, Implicit conversion from nullable pointer 'TypeA * _Nullable' to non-nullable pointer type 'TypeA * _Nonnull'
,
Я начал свою попытку удаления с этих предупреждений, создав локальный файл того же типа в методе, как описано здесь. https://www.mail-archive.com/xcode-users%40lists.apple.com/msg02260.html этой статье говорится, что сначала при использовании локального объекта объект имеет неопределенный атрибут, который может быть обнуляем, поэтому его можно использовать в качестве легального параметра. к методам, ожидающим ненулевое.
Но я чувствую, что это отстраненный ход и действительно не решает проблему каким-либо выгодным способом.
Кто-нибудь уже прошел это упражнение? Я был бы признателен, если бы вы могли поделиться стратегией, которую вы взяли.
2 ответа
Не каждое предупреждение имеет смысл. Иногда это недостаток в компиляторе. Например, этот код не нуждается в предупреждении.
- (nullable id)transformedValue:(nullable id)value {
id result = value != nil ? UIImageJPEGRepresentation(value, 1.0) : nil;
return result;
}
Мы проверяем, является ли оно нулевым! Что еще мы можем сделать? Зачем создавать дополнительный указатель?
Итак, мы делаем это:
- (nullable id)transformedValue:(nullable id)value {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnullable-to-nonnull-conversion"
id result = value != nil ? UIImageJPEGRepresentation(value, 1.0) : nil;
#pragma clang diagnostic pop
return result;
}
Почему это правильный ответ?
Во-первых, нормально быть умнее компилятора. Вы не хотите начинать уничтожать ваш код, просто из-за поддельного предупреждения.
Это решение указывает точное предупреждающее сообщение для подавления, и оно подавляет его только для одной строки.
На самом деле, я немного повозился с этой темой. Я хотел улучшить ситуацию обнуляемости в довольно большом проекте (сделать его более "быстрым"). Вот что я нашел.
Во-первых, вы должны включить CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION
(-Wnullable-to-nonnull-conversion
)
Во-вторых, о
Во-первых, используя локальный, этот объект имеет неопределенный атрибут nullable, поэтому его можно использовать в качестве допустимого параметра для методов, ожидающих ненулевое значение.
Это плохо пахнет, и я создал макрос под названием NONNUL_CAST()
, Вот пример, как это реализовать:
#define NONNUL_CAST(__var) ({ NSCAssert(__var, @"Variable is nil");\
(__typeof(*(__var))* _Nonnull)__var; })
Здесь вы можете увидеть хаки __typeof(*(__var))* _Nonnull)__var
, но это не так плохо. Если __var
имеет тип A* _Nullable
мы разыменовываем __var
так что это типа сейчас просто A
после того, как мы сделаем ссылку снова, но _Nonnull
и получить ненулевой __var
как ответ. Конечно, мы утверждаем, если что-то пойдет не так.
В-третьих, вы должны указать nullabilty для каждой локальной переменной, и вы должны поместить весь свой код в NS_ASSUME_NONNULL_BEGIN/END
, как это:
NS_ASSUME_NONNULL_BEGIN
<your_code_goes_here>
NS_ASSUME_NONNULL_END`
Вы помещаете туда КАЖДУЮ ЛИНИЮ (кроме импорта) вашего кода, в .h
а также .m
файлы. Это предполагает, что все ваши аргументы методов, возвращаемых типов и свойств не равны нулю. Если вы хотите сделать его обнуляемым, поместите туда обнуляемый.
Итак, все сделано, что теперь? Вот пример типичного использования:
- (Atype*)makeAtypeWithBtype:(nullable BType*)btype {
Atype* _Nullable a = [btype makeAtype];
if (a) {
// All good here.
return NONNUL_CAST(a);
} else {
// a appeared as nil. Some fallback is needed.
[self reportError];
return [AtypeFactory makeDeafult];
}
}
Теперь у вас есть более надежная ситуация обнуления. Может быть, это не выглядит красиво, но это объективно, так что не на что жаловаться.