Jak używać niepustych i dopuszczających wartość null słów kluczowych Objective-C w metodzie interfejsu API opartej na blokach

106

Rozważ następującą metodę

- (void)methodWithArg:(NSString *)arg1 andArg:(NSString *)arg2 completionHandler:(void (^)(NSArray *results, NSError *error))completionHandler;

Dzięki nowym słowom kluczowymnonnull i nullable adnotacjom możemy go wzbogacić w następujący sposób:

- (void)methodWithArg:(nonnull NSString *)arg1 andArg:(nullable NSString *)arg2 completionHandler:(void (^)(NSArray *results, NSError *error))completionHandler;

ale otrzymujemy również to ostrzeżenie:

We wskaźniku brakuje specyfikatora typu dopuszczalności wartości null (__nonnull lub __nullable)

Odnosi się do trzeciego parametru (blokowego).

Dokumentacja nie obejmuje przykłady jak określić opcje dopuszczania wartości null parametrów bloku. Mówi dosłownie

Formularz nie podkreślonych można używać bezpośrednio po otwartym nawiasie, o ile typ jest prostym obiektem lub wskaźnikiem do bloku.

Próbowałem umieścić jedno z dwóch słów kluczowych dla bloku (w dowolnej pozycji) bez powodzenia. Wypróbowałem także warianty z przedrostkiem podkreślonym ( __nonnulli __nullable).

Dlatego moje pytanie brzmi: jak mogę określić semantyczną wartość zerową dla parametrów bloku?

albertodebortoli
źródło

Odpowiedzi:

128

To wydaje się działać

- (void)methodWithArg:(nonnull NSString *)arg1 
  andArg:(nullable NSString *)arg2 completionHandler:(nullable void (^)
  (NSArray * _Nullable results, NSError * _Nonnull error))completionHandler

Musisz określić dopuszczalność wartości null zarówno dla bloku, jak i jego parametrów ...

EDYCJA: Aby uzyskać więcej informacji, zobacz Swift Blog

Fabio Ritrovato
źródło
Jak to działa z NSError **typem? Wydaje się, że nie mogę uszczęśliwić kompilatora.
duhanebel
3
Według szybkiego bloga: The particular type NSError ** is so often used to return errors via method parameters that it is always assumed to be a nullable pointer to a nullable NSError reference. developer.apple.com/swift/blog/?id=25
user1687195
@duhanebel Odpowiedź znajduje się w stackoverflow.com/questions/33198597/… : (NSError * _Nullable * _Nullable) błąd
Elise van Looij
33

Według Apple Blog („Nullability and Objective-C”) możesz użyć

NS_ASSUME_NONNULL_BEGINi NS_ASSUME_NONNULL_END.

W tych regionach przyjmuje się, że każdy prosty typ wskaźnika jest nonnull. Następnie możesz po prostu dodać nullableobiekt dopuszczający wartość null, taki jak

NS_ASSUME_NONNULL_BEGIN

@interface MyClass: NSObject

- (void)methodWithArg:(NSString *)arg1 andArg:(nullable NSString *)arg2 completionHandler:(void (^)(NSArray *results, NSError *error))completionHandler;

@end

NS_ASSUME_NONNULL_END
  • jeśli błąd jest NSError **typem, powinien byćNSError * _Nullable * _Nullable
  • jeśli obiekt jest id *typem, lepiej użyj id _Nullable * _Nonnull, to zależy (może chcesz _Nullable id * _Nullabletyp).
  • jeśli obiekt jest NSObject *typem, musisz umieścić adnotację po wskaźniku, w ten sposóbNSObject * _Nullable * _Nonnull

Uwaga

_Nonnulli _Nullablepowinien być używany po wskaźniku lub id(Apple robi to w przykładowym kodzie AAPLListItem * _Nullable), ale formularze bez podkreślenia nonnulli nullablemogą być używane po otwartym nawiasie.

Jednak w typowym przypadku istnieje znacznie przyjemniejszy sposób pisania tych adnotacji: w deklaracjach metod można używać formularzy bez podkreślenia nullablei nonnullbezpośrednio po otwartym nawiasie, o ile typ jest prostym obiektem lub wskaźnikiem blokowym.

zobacz więcej w „Nullability and Objective-C”

Ze względów bezpieczeństwa istnieje kilka wyjątków od tej zasady:

  • typedeftypy zwykle nie mają nieodłącznej wartości null - mogą łatwo dopuszczać wartość null lub non-nullable w zależności od kontekstu. Dlatego typedefnie zakłada się , że typy są nonnull, nawet w kontrolowanych regionach.
  • Bardziej złożone typy wskaźników, takie jak id *muszą być jawnie opisane. Na przykład, aby określić niedopuszczalny wskaźnik do odwołania do obiektu dopuszczającego wartość null, użyj _Nullable id * _Nonnull.
  • Określony typ NSError **jest tak często używany do zwracania błędów za pośrednictwem parametrów metody, że zawsze przyjmuje się, że jest wskaźnikiem dopuszczającym wartość null do NSErrorodwołania dopuszczającego wartość null .

_Nullable id * _NonnullMoże być mylony, id _Nullable * _Nonnulljest lepsze zrozumienie.

_Nonnulli _Nullablepowinien być używany po wskaźniku lub id(Apple robi to w przykładowym kodzieAAPLListItem * _Nullable )

likid1412
źródło
W przypadku słabej właściwości zastosowano _Nullable.
Dongjin Suh
3

Możesz też zrobić tak:

- (id __nullable)methodWithArg:(NSString * __nullable)arg1
                        andArg:(NSString * __nonnull)arg2
             completionHandler:(void (^ __nonnull)(NSArray * __nonnull results, NSError * __nullable error))completionHandler;

Zależy to tylko od tego, którą składnię lubisz bardziej.

OlDor
źródło
2

Aby zdefiniować uzupełnienia w pliku nagłówkowym, zrobiłem to

typedef void (^PublicEventsHandler) (BOOL success, NSArray * _Nullable publicEvents);

Oczywiście zgadzam się z zaakceptowaną odpowiedzią.

Krishna Kumar
źródło
0

Z bloga programistów Apple : The Core: _Nullable and _Nonnull

możesz użyć niewidocznych formularzy, które mogą mieć wartość nullable i nonnull, bezpośrednio po otwartym nawiasie , o ile typ jest prostym obiektem lub wskaźnikiem do bloku.

Formularze bez podkreślenia są ładniejsze niż te z podkreśleniem, ale nadal musisz zastosować je do każdego typu w nagłówku .

Parag Bafna
źródło
Tak, ale te bez podkreślenia (ładniejsze) nie działają w deklaracjach blokowych
Paul Bruneau
-2

Oto, czego użyłem w przypadku NSError **:

-(BOOL) something:(int)number withError:(NSError *__autoreleasing  __nullable * __nullable)error;
Kevin
źródło
1
Jak powiedział Apple , NSError **nie musisz określać jego wartości zerowej.
DawnSong