Mercurial > hg > CbC > CbC_llvm
diff clang/docs/ObjectiveCLiterals.rst @ 150:1d019706d866
LLVM10
author | anatofuz |
---|---|
date | Thu, 13 Feb 2020 15:10:13 +0900 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/clang/docs/ObjectiveCLiterals.rst Thu Feb 13 15:10:13 2020 +0900 @@ -0,0 +1,606 @@ +==================== +Objective-C Literals +==================== + +Introduction +============ + +Three new features were introduced into clang at the same time: +*NSNumber Literals* provide a syntax for creating ``NSNumber`` from +scalar literal expressions; *Collection Literals* provide a short-hand +for creating arrays and dictionaries; *Object Subscripting* provides a +way to use subscripting with Objective-C objects. Users of Apple +compiler releases can use these features starting with the Apple LLVM +Compiler 4.0. Users of open-source LLVM.org compiler releases can use +these features starting with clang v3.1. + +These language additions simplify common Objective-C programming +patterns, make programs more concise, and improve the safety of +container creation. + +This document describes how the features are implemented in clang, and +how to use them in your own programs. + +NSNumber Literals +================= + +The framework class ``NSNumber`` is used to wrap scalar values inside +objects: signed and unsigned integers (``char``, ``short``, ``int``, +``long``, ``long long``), floating point numbers (``float``, +``double``), and boolean values (``BOOL``, C++ ``bool``). Scalar values +wrapped in objects are also known as *boxed* values. + +In Objective-C, any character, numeric or boolean literal prefixed with +the ``'@'`` character will evaluate to a pointer to an ``NSNumber`` +object initialized with that value. C's type suffixes may be used to +control the size of numeric literals. + +Examples +-------- + +The following program illustrates the rules for ``NSNumber`` literals: + +.. code-block:: objc + + void main(int argc, const char *argv[]) { + // character literals. + NSNumber *theLetterZ = @'Z'; // equivalent to [NSNumber numberWithChar:'Z'] + + // integral literals. + NSNumber *fortyTwo = @42; // equivalent to [NSNumber numberWithInt:42] + NSNumber *fortyTwoUnsigned = @42U; // equivalent to [NSNumber numberWithUnsignedInt:42U] + NSNumber *fortyTwoLong = @42L; // equivalent to [NSNumber numberWithLong:42L] + NSNumber *fortyTwoLongLong = @42LL; // equivalent to [NSNumber numberWithLongLong:42LL] + + // floating point literals. + NSNumber *piFloat = @3.141592654F; // equivalent to [NSNumber numberWithFloat:3.141592654F] + NSNumber *piDouble = @3.1415926535; // equivalent to [NSNumber numberWithDouble:3.1415926535] + + // BOOL literals. + NSNumber *yesNumber = @YES; // equivalent to [NSNumber numberWithBool:YES] + NSNumber *noNumber = @NO; // equivalent to [NSNumber numberWithBool:NO] + + #ifdef __cplusplus + NSNumber *trueNumber = @true; // equivalent to [NSNumber numberWithBool:(BOOL)true] + NSNumber *falseNumber = @false; // equivalent to [NSNumber numberWithBool:(BOOL)false] + #endif + } + +Discussion +---------- + +NSNumber literals only support literal scalar values after the ``'@'``. +Consequently, ``@INT_MAX`` works, but ``@INT_MIN`` does not, because +they are defined like this: + +.. code-block:: objc + + #define INT_MAX 2147483647 /* max value for an int */ + #define INT_MIN (-2147483647-1) /* min value for an int */ + +The definition of ``INT_MIN`` is not a simple literal, but a +parenthesized expression. Parenthesized expressions are supported using +the `boxed expression <#objc_boxed_expressions>`_ syntax, which is +described in the next section. + +Because ``NSNumber`` does not currently support wrapping ``long double`` +values, the use of a ``long double NSNumber`` literal (e.g. +``@123.23L``) will be rejected by the compiler. + +Previously, the ``BOOL`` type was simply a typedef for ``signed char``, +and ``YES`` and ``NO`` were macros that expand to ``(BOOL)1`` and +``(BOOL)0`` respectively. To support ``@YES`` and ``@NO`` expressions, +these macros are now defined using new language keywords in +``<objc/objc.h>``: + +.. code-block:: objc + + #if __has_feature(objc_bool) + #define YES __objc_yes + #define NO __objc_no + #else + #define YES ((BOOL)1) + #define NO ((BOOL)0) + #endif + +The compiler implicitly converts ``__objc_yes`` and ``__objc_no`` to +``(BOOL)1`` and ``(BOOL)0``. The keywords are used to disambiguate +``BOOL`` and integer literals. + +Objective-C++ also supports ``@true`` and ``@false`` expressions, which +are equivalent to ``@YES`` and ``@NO``. + +Boxed Expressions +================= + +Objective-C provides a new syntax for boxing C expressions: + +.. code-block:: objc + + @( <expression> ) + +Expressions of scalar (numeric, enumerated, BOOL), C string pointer +and some C structures (via NSValue) are supported: + +.. code-block:: objc + + // numbers. + NSNumber *smallestInt = @(-INT_MAX - 1); // [NSNumber numberWithInt:(-INT_MAX - 1)] + NSNumber *piOverTwo = @(M_PI / 2); // [NSNumber numberWithDouble:(M_PI / 2)] + + // enumerated types. + typedef enum { Red, Green, Blue } Color; + NSNumber *favoriteColor = @(Green); // [NSNumber numberWithInt:((int)Green)] + + // strings. + NSString *path = @(getenv("PATH")); // [NSString stringWithUTF8String:(getenv("PATH"))] + NSArray *pathComponents = [path componentsSeparatedByString:@":"]; + + // structs. + NSValue *center = @(view.center); // Point p = view.center; + // [NSValue valueWithBytes:&p objCType:@encode(Point)]; + NSValue *frame = @(view.frame); // Rect r = view.frame; + // [NSValue valueWithBytes:&r objCType:@encode(Rect)]; + +Boxed Enums +----------- + +Cocoa frameworks frequently define constant values using *enums.* +Although enum values are integral, they may not be used directly as +boxed literals (this avoids conflicts with future ``'@'``-prefixed +Objective-C keywords). Instead, an enum value must be placed inside a +boxed expression. The following example demonstrates configuring an +``AVAudioRecorder`` using a dictionary that contains a boxed enumeration +value: + +.. code-block:: objc + + enum { + AVAudioQualityMin = 0, + AVAudioQualityLow = 0x20, + AVAudioQualityMedium = 0x40, + AVAudioQualityHigh = 0x60, + AVAudioQualityMax = 0x7F + }; + + - (AVAudioRecorder *)recordToFile:(NSURL *)fileURL { + NSDictionary *settings = @{ AVEncoderAudioQualityKey : @(AVAudioQualityMax) }; + return [[AVAudioRecorder alloc] initWithURL:fileURL settings:settings error:NULL]; + } + +The expression ``@(AVAudioQualityMax)`` converts ``AVAudioQualityMax`` +to an integer type, and boxes the value accordingly. If the enum has a +:ref:`fixed underlying type <objc-fixed-enum>` as in: + +.. code-block:: objc + + typedef enum : unsigned char { Red, Green, Blue } Color; + NSNumber *red = @(Red), *green = @(Green), *blue = @(Blue); // => [NSNumber numberWithUnsignedChar:] + +then the fixed underlying type will be used to select the correct +``NSNumber`` creation method. + +Boxing a value of enum type will result in a ``NSNumber`` pointer with a +creation method according to the underlying type of the enum, which can +be a :ref:`fixed underlying type <objc-fixed-enum>` +or a compiler-defined integer type capable of representing the values of +all the members of the enumeration: + +.. code-block:: objc + + typedef enum : unsigned char { Red, Green, Blue } Color; + Color col = Red; + NSNumber *nsCol = @(col); // => [NSNumber numberWithUnsignedChar:] + +Boxed C Strings +--------------- + +A C string literal prefixed by the ``'@'`` token denotes an ``NSString`` +literal in the same way a numeric literal prefixed by the ``'@'`` token +denotes an ``NSNumber`` literal. When the type of the parenthesized +expression is ``(char *)`` or ``(const char *)``, the result of the +boxed expression is a pointer to an ``NSString`` object containing +equivalent character data, which is assumed to be '\\0'-terminated and +UTF-8 encoded. The following example converts C-style command line +arguments into ``NSString`` objects. + +.. code-block:: objc + + // Partition command line arguments into positional and option arguments. + NSMutableArray *args = [NSMutableArray new]; + NSMutableDictionary *options = [NSMutableDictionary new]; + while (--argc) { + const char *arg = *++argv; + if (strncmp(arg, "--", 2) == 0) { + options[@(arg + 2)] = @(*++argv); // --key value + } else { + [args addObject:@(arg)]; // positional argument + } + } + +As with all C pointers, character pointer expressions can involve +arbitrary pointer arithmetic, therefore programmers must ensure that the +character data is valid. Passing ``NULL`` as the character pointer will +raise an exception at runtime. When possible, the compiler will reject +``NULL`` character pointers used in boxed expressions. + +Boxed C Structures +------------------ + +Boxed expressions support construction of NSValue objects. +It said that C structures can be used, the only requirement is: +structure should be marked with ``objc_boxable`` attribute. +To support older version of frameworks and/or third-party libraries +you may need to add the attribute via ``typedef``. + +.. code-block:: objc + + struct __attribute__((objc_boxable)) Point { + // ... + }; + + typedef struct __attribute__((objc_boxable)) _Size { + // ... + } Size; + + typedef struct _Rect { + // ... + } Rect; + + struct Point p; + NSValue *point = @(p); // ok + Size s; + NSValue *size = @(s); // ok + + Rect r; + NSValue *bad_rect = @(r); // error + + typedef struct __attribute__((objc_boxable)) _Rect Rect; + + NSValue *good_rect = @(r); // ok + + +Container Literals +================== + +Objective-C now supports a new expression syntax for creating immutable +array and dictionary container objects. + +Examples +-------- + +Immutable array expression: + +.. code-block:: objc + + NSArray *array = @[ @"Hello", NSApp, [NSNumber numberWithInt:42] ]; + +This creates an ``NSArray`` with 3 elements. The comma-separated +sub-expressions of an array literal can be any Objective-C object +pointer typed expression. + +Immutable dictionary expression: + +.. code-block:: objc + + NSDictionary *dictionary = @{ + @"name" : NSUserName(), + @"date" : [NSDate date], + @"processInfo" : [NSProcessInfo processInfo] + }; + +This creates an ``NSDictionary`` with 3 key/value pairs. Value +sub-expressions of a dictionary literal must be Objective-C object +pointer typed, as in array literals. Key sub-expressions must be of an +Objective-C object pointer type that implements the +``<NSCopying>`` protocol. + +Discussion +---------- + +Neither keys nor values can have the value ``nil`` in containers. If the +compiler can prove that a key or value is ``nil`` at compile time, then +a warning will be emitted. Otherwise, a runtime error will occur. + +Using array and dictionary literals is safer than the variadic creation +forms commonly in use today. Array literal expressions expand to calls +to ``+[NSArray arrayWithObjects:count:]``, which validates that all +objects are non-``nil``. The variadic form, +``+[NSArray arrayWithObjects:]`` uses ``nil`` as an argument list +terminator, which can lead to malformed array objects. Dictionary +literals are similarly created with +``+[NSDictionary dictionaryWithObjects:forKeys:count:]`` which validates +all objects and keys, unlike +``+[NSDictionary dictionaryWithObjectsAndKeys:]`` which also uses a +``nil`` parameter as an argument list terminator. + +Object Subscripting +=================== + +Objective-C object pointer values can now be used with C's subscripting +operator. + +Examples +-------- + +The following code demonstrates the use of object subscripting syntax +with ``NSMutableArray`` and ``NSMutableDictionary`` objects: + +.. code-block:: objc + + NSMutableArray *array = ...; + NSUInteger idx = ...; + id newObject = ...; + id oldObject = array[idx]; + array[idx] = newObject; // replace oldObject with newObject + + NSMutableDictionary *dictionary = ...; + NSString *key = ...; + oldObject = dictionary[key]; + dictionary[key] = newObject; // replace oldObject with newObject + +The next section explains how subscripting expressions map to accessor +methods. + +Subscripting Methods +-------------------- + +Objective-C supports two kinds of subscript expressions: *array-style* +subscript expressions use integer typed subscripts; *dictionary-style* +subscript expressions use Objective-C object pointer typed subscripts. +Each type of subscript expression is mapped to a message send using a +predefined selector. The advantage of this design is flexibility: class +designers are free to introduce subscripting by declaring methods or by +adopting protocols. Moreover, because the method names are selected by +the type of the subscript, an object can be subscripted using both array +and dictionary styles. + +Array-Style Subscripting +^^^^^^^^^^^^^^^^^^^^^^^^ + +When the subscript operand has an integral type, the expression is +rewritten to use one of two different selectors, depending on whether +the element is being read or written. When an expression reads an +element using an integral index, as in the following example: + +.. code-block:: objc + + NSUInteger idx = ...; + id value = object[idx]; + +it is translated into a call to ``objectAtIndexedSubscript:`` + +.. code-block:: objc + + id value = [object objectAtIndexedSubscript:idx]; + +When an expression writes an element using an integral index: + +.. code-block:: objc + + object[idx] = newValue; + +it is translated to a call to ``setObject:atIndexedSubscript:`` + +.. code-block:: objc + + [object setObject:newValue atIndexedSubscript:idx]; + +These message sends are then type-checked and performed just like +explicit message sends. The method used for objectAtIndexedSubscript: +must be declared with an argument of integral type and a return value of +some Objective-C object pointer type. The method used for +setObject:atIndexedSubscript: must be declared with its first argument +having some Objective-C pointer type and its second argument having +integral type. + +The meaning of indexes is left up to the declaring class. The compiler +will coerce the index to the appropriate argument type of the method it +uses for type-checking. For an instance of ``NSArray``, reading an +element using an index outside the range ``[0, array.count)`` will raise +an exception. For an instance of ``NSMutableArray``, assigning to an +element using an index within this range will replace that element, but +assigning to an element using an index outside this range will raise an +exception; no syntax is provided for inserting, appending, or removing +elements for mutable arrays. + +A class need not declare both methods in order to take advantage of this +language feature. For example, the class ``NSArray`` declares only +``objectAtIndexedSubscript:``, so that assignments to elements will fail +to type-check; moreover, its subclass ``NSMutableArray`` declares +``setObject:atIndexedSubscript:``. + +Dictionary-Style Subscripting +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When the subscript operand has an Objective-C object pointer type, the +expression is rewritten to use one of two different selectors, depending +on whether the element is being read from or written to. When an +expression reads an element using an Objective-C object pointer +subscript operand, as in the following example: + +.. code-block:: objc + + id key = ...; + id value = object[key]; + +it is translated into a call to the ``objectForKeyedSubscript:`` method: + +.. code-block:: objc + + id value = [object objectForKeyedSubscript:key]; + +When an expression writes an element using an Objective-C object pointer +subscript: + +.. code-block:: objc + + object[key] = newValue; + +it is translated to a call to ``setObject:forKeyedSubscript:`` + +.. code-block:: objc + + [object setObject:newValue forKeyedSubscript:key]; + +The behavior of ``setObject:forKeyedSubscript:`` is class-specific; but +in general it should replace an existing value if one is already +associated with a key, otherwise it should add a new value for the key. +No syntax is provided for removing elements from mutable dictionaries. + +Discussion +---------- + +An Objective-C subscript expression occurs when the base operand of the +C subscript operator has an Objective-C object pointer type. Since this +potentially collides with pointer arithmetic on the value, these +expressions are only supported under the modern Objective-C runtime, +which categorically forbids such arithmetic. + +Currently, only subscripts of integral or Objective-C object pointer +type are supported. In C++, a class type can be used if it has a single +conversion function to an integral or Objective-C pointer type, in which +case that conversion is applied and analysis continues as appropriate. +Otherwise, the expression is ill-formed. + +An Objective-C object subscript expression is always an l-value. If the +expression appears on the left-hand side of a simple assignment operator +(=), the element is written as described below. If the expression +appears on the left-hand side of a compound assignment operator (e.g. ++=), the program is ill-formed, because the result of reading an element +is always an Objective-C object pointer and no binary operators are +legal on such pointers. If the expression appears in any other position, +the element is read as described below. It is an error to take the +address of a subscript expression, or (in C++) to bind a reference to +it. + +Programs can use object subscripting with Objective-C object pointers of +type ``id``. Normal dynamic message send rules apply; the compiler must +see *some* declaration of the subscripting methods, and will pick the +declaration seen first. + +Caveats +======= + +Objects created using the literal or boxed expression syntax are not +guaranteed to be uniqued by the runtime, but nor are they guaranteed to +be newly-allocated. As such, the result of performing direct comparisons +against the location of an object literal (using ``==``, ``!=``, ``<``, +``<=``, ``>``, or ``>=``) is not well-defined. This is usually a simple +mistake in code that intended to call the ``isEqual:`` method (or the +``compare:`` method). + +This caveat applies to compile-time string literals as well. +Historically, string literals (using the ``@"..."`` syntax) have been +uniqued across translation units during linking. This is an +implementation detail of the compiler and should not be relied upon. If +you are using such code, please use global string constants instead +(``NSString * const MyConst = @"..."``) or use ``isEqual:``. + +Grammar Additions +================= + +To support the new syntax described above, the Objective-C +``@``-expression grammar has the following new productions: + +:: + + objc-at-expression : '@' (string-literal | encode-literal | selector-literal | protocol-literal | object-literal) + ; + + object-literal : ('+' | '-')? numeric-constant + | character-constant + | boolean-constant + | array-literal + | dictionary-literal + ; + + boolean-constant : '__objc_yes' | '__objc_no' | 'true' | 'false' /* boolean keywords. */ + ; + + array-literal : '[' assignment-expression-list ']' + ; + + assignment-expression-list : assignment-expression (',' assignment-expression-list)? + | /* empty */ + ; + + dictionary-literal : '{' key-value-list '}' + ; + + key-value-list : key-value-pair (',' key-value-list)? + | /* empty */ + ; + + key-value-pair : assignment-expression ':' assignment-expression + ; + +Note: ``@true`` and ``@false`` are only supported in Objective-C++. + +Availability Checks +=================== + +Programs test for the new features by using clang's \_\_has\_feature +checks. Here are examples of their use: + +.. code-block:: objc + + #if __has_feature(objc_array_literals) + // new way. + NSArray *elements = @[ @"H", @"He", @"O", @"C" ]; + #else + // old way (equivalent). + id objects[] = { @"H", @"He", @"O", @"C" }; + NSArray *elements = [NSArray arrayWithObjects:objects count:4]; + #endif + + #if __has_feature(objc_dictionary_literals) + // new way. + NSDictionary *masses = @{ @"H" : @1.0078, @"He" : @4.0026, @"O" : @15.9990, @"C" : @12.0096 }; + #else + // old way (equivalent). + id keys[] = { @"H", @"He", @"O", @"C" }; + id values[] = { [NSNumber numberWithDouble:1.0078], [NSNumber numberWithDouble:4.0026], + [NSNumber numberWithDouble:15.9990], [NSNumber numberWithDouble:12.0096] }; + NSDictionary *masses = [NSDictionary dictionaryWithObjects:objects forKeys:keys count:4]; + #endif + + #if __has_feature(objc_subscripting) + NSUInteger i, count = elements.count; + for (i = 0; i < count; ++i) { + NSString *element = elements[i]; + NSNumber *mass = masses[element]; + NSLog(@"the mass of %@ is %@", element, mass); + } + #else + NSUInteger i, count = [elements count]; + for (i = 0; i < count; ++i) { + NSString *element = [elements objectAtIndex:i]; + NSNumber *mass = [masses objectForKey:element]; + NSLog(@"the mass of %@ is %@", element, mass); + } + #endif + + #if __has_attribute(objc_boxable) + typedef struct __attribute__((objc_boxable)) _Rect Rect; + #endif + + #if __has_feature(objc_boxed_nsvalue_expressions) + CABasicAnimation animation = [CABasicAnimation animationWithKeyPath:@"position"]; + animation.fromValue = @(layer.position); + animation.toValue = @(newPosition); + [layer addAnimation:animation forKey:@"move"]; + #else + CABasicAnimation animation = [CABasicAnimation animationWithKeyPath:@"position"]; + animation.fromValue = [NSValue valueWithCGPoint:layer.position]; + animation.toValue = [NSValue valueWithCGPoint:newPosition]; + [layer addAnimation:animation forKey:@"move"]; + #endif + +Code can use also ``__has_feature(objc_bool)`` to check for the +availability of numeric literals support. This checks for the new +``__objc_yes / __objc_no`` keywords, which enable the use of +``@YES / @NO`` literals. + +To check whether boxed expressions are supported, use +``__has_feature(objc_boxed_expressions)`` feature macro.