typedef enum {
EXApple, EXPear, EXOrange
} EXFruit;
The mapping for structures maps Slice structures to Objective‑C classes. For each Slice data member, the Objective‑C class has a corresponding property. For example, here is our
Employee structure from
Section 4.9.4 once more:
struct Employee {
long number;
string firstName;
string lastName;
};
The Slice-to-Objective‑C compiler generates the following definition for this structure:
@interface EXEmployee : NSObject <NSCopying>
{
@private
ICELong number;
NSString *firstName;
NSString *lastName;
}
@property(nonatomic, assign) ICELong number;
@property(nonatomic, retain) NSString *firstName;
@property(nonatomic, retain) NSString *lastName;
‑(id) init:(ICELong)number firstName:(NSString *)firstName
lastName:(NSString *)lastName;
+(id) employee:(ICELong)number firstName:(NSString *)firstName
lastName:(NSString *)lastName;
+(id) employee;
// This class also overrides copyWithZone,
// hash, isequal, and dealloc.
@end
For each data member in the Slice definition, the Objective‑C class contains a corresponding private instance variable of the same name, as well as a property definition that allows you to set and get the value of the corresponding instance variable. For example, given an instance of
EXEmployee, you can write the following:
ICELong number;
EXemployee *e = ...;
[e setNumber:99];
number = [e number];
// Or, more concisely with dot notation:
e.number = 99;
number = e.number;
Properties that represent data members always use the nonatomic property attribute. This avoids the overhead of locking each data member during access. The second property attribute is
assign for integral and floating-point types and
retain for all other types (such as strings, structures, and so on.)
EXEmployee *e = [[EXEmployee alloc] init];
// ...
[e release];
As usual, init initializes the instance variables of the structure with zero-filled memory.
In addition, a structure provides a second init method that accepts one parameter for each data member of the structure:
‑(id) init:(ICELong)number firstName:(NSString *)firstName
lastName:(NSString *)lastName;
Note that the first parameter is always unlabeled; the second and subsequent parameters have a label that is the same as the name of the corresponding Slice data member. The additional
init method allows you to instantiate a structure and initialize its data members in a single statement:
EXEmployee *e = [[EXEmployee alloc] init:99 firstName:@"Brad"
lastName:@"Cox"];
// ...
[e release];
init applies the memory management policy of the corresponding properties, that is, it calls
retain on the
firstName and
lastName arguments.
Each structure also provides two convenience constructors that mirror the init methods: a parameter-less convenience constructor and one that has a parameter for each Slice data member:
+(id) employee;
+(id) employee:(ICELong)number firstName:(NSString *)firstName
lastName:(NSString *)lastName;
The convenience constructors have the same name as the mapped Slice structure (without the module prefix). As usual, they allocate an instance, perform the same initialization actions as the corresponding
init methods, and call
autorelease on the return value:
EXEmployee *e = [EXEmployee employee:99 firstName:@"Brad"
lastName:@"Cox"];
// No need to call [e release] here.
Structures implement the NSCopying protocol. Structures are copied by assigning instance variables of value type and calling
retain on each instance variable of non-value type. In other words, the copy is shallow:
EXEmployee *e = [EXEmployee employee:99 firstName:@"Brad"
lastName:@"Cox"];
EXEmployee *e2 = [e copy];
NSAssert(e.number == e2.number);
NSAssert([e.firstName == e2.firstName]); // Same instance
// ...
[e2 release];
Note that, if you assign an NSMutableString to a structure member and use the structure as a dictionary key, you must not modify the string inside the structure without copying it because doing so will corrupt the dictionary.
Each structure implements a dealloc method that calls
release on each instance variable with a
retain property attribute. This means that structures take care of the memory management of their contents: releasing a structure automatically releases all its instance variables.
Structures implement isEqual, so you can compare them for equality. Two structures are equal if all their instance variables are equal. For value types, equality is determined by the
== operator; for non-value types other than classes, equality is determined by the corresponding instance variable’s
isEqual method. Classes (see
Section 18.15) are compared by comparing their identity: two class members are equal if they both point at the same instance.
The hash method returns a hash value is that is computed from the hash value of all of the structure’s instance variables.
The Objective‑C mapping uses different mappings for sequence of value types (such as
sequence<byte>) and non-value types (such as
sequence<string>).
enum Fruit { Apple, Pear, Orange };
sequence<byte> ByteSeq;
sequence<int> IntSeq;
sequence<Fruit> FruitSeq;
typedef enum {
EXApple, EXPear, EXOrange
} EXFruit;
typedef NSData EXByteSeq;
typedef NSMutableData EXMutableByteSeq;
typedef NSData EXIntSeq;
typedef NSMutableData EXMutableIntSeq;
typedef NSData EXFruitSeq;
typedef NSMutableData EXMutableFruitSeq;
As you can see, each sequence definition creates a pair of type definitions, an immutable version named
<module‑prefix><Slice‑name>, and a mutable version named
<module‑prefix>Mutable<Slice‑name>. This constitutes the entire public API for sequence of value types, that is, sequences of value types simply map to
NSData or
NSMutableData. The
NS(
Mutable)
Data sequences contain an array of the corresponding element type in their internal byte array.
1
For example, here is how you could initialize a byte sequence of 1024 elements with values that are the modulo 128 of the element index in reverse order:
int limit = 1024;
EXMutableByteSeq *bs = [NSMutableData dataWithLength:limit];
ICEByte *p = (ICEByte *)[bs bytes];
while (‑‑limit > 0) {
*p++ = limit % 0x80;
}
Naturally, you do not need to initialize the sequence using a loop. For example, if the data is available in a buffer, you could use the
dataWithBytes:length or
dataWithBytesNoCopy:length methods of
NSData instead.
const ICEByte* p = (const ICEByte *)[bs bytes];
const ICEByte* limitp = p + [bs length];
while (p < limitp) {
printf("%d\n", *p++);
}
For sequences of types other than byte or
bool, you must keep in mind that the length of the
NSData array is not the same as the number of elements. The following example initializes an integer sequence with the first few primes and prints out the contents of the sequence:
const int primes[] = { 1, 2, 3, 5, 7, 9, 11, 13, 17, 19, 23 };
EXMutableIntSeq *is = [NSMutableData dataWithBytes:primes
length:sizeof(primes)];
const ICEInt *p = (const ICEInt *)[is bytes];
int limit = [is length] / sizeof(*p);
int i;
for(i = 0; i < limit; ++i) {
printf("%d\n", p[i]);
}
The code to manipulate a sequence of enumerators is very similar. For portability, you should not assume a particular size for enumerators. That is, instead of relying on all enumerators having the size of, for example, an
int, it is better to use
sizeof(EXFruit) to ensure that you are not overstepping the bounds of the sequence.
Sequences of non-value types, such as sequences of string, structures, classes, and so on, map to mutable and immutable type definitions of
NSArray. For example:
typedef NSArray EXPage;
typedef NSMutableArray EXMutablePage;
typedef NSArray EXBook;
typedef NSMutableArray EXMutableBook;
EXMutablePage *page1 = [NSArray arrayWithObjects:
@"First line of page one",
@"Second line of page one",
nil];
EXMutablePage *page2 = [NSArray arrayWithObjects:
@"First line of page two",
@"Second line of page two",
nil];
EXMutableBook *book = [NSMutableArray array];
[book addObject:page1];
[book addObject:page2];
[book addObject:[NSArray array]]; // Empty page
int pageNum = 0;
for (EXPage *page in book) {
++pageNum;
int lineNum = 0;
if ([page count] == 0) {
printf("page %d: <empty>\n", pageNum);
} else {
for (NSString *line in page) {
++lineNum;
printf("page %d, line %d: %s\n",
pageNum, lineNum, [line UTF8String]);
}
}
}
page 1, line 1: First line of page one
page 1, line 2: Second line of page one
page 2, line 1: First line of page two
page 2, line 2: Second line of page two
page 3: <empty>
If you have a sequence of proxies or a sequence of classes, to transmit a null proxy or class inside a sequence, you must insert an
NSNull value into the
NSArray. In addition, the mapping also allows you to use
NSNull as the element value of an
NSArray for elements of type string, structure, sequence, or dictionary. For example, instead of inserting an empty
NSArray into the book sequence in the preceding example, we could also have inserted
NSNull:
EXMutableBook *book = [NSMutableArray array];
[book addObject:page1];
[book addObject:page2];
[book addObject:[NSNull null]]; // Empty page
typedef NSDictionary EXEmployeeMap;
typedef NSMutableDictionary EXMutableEmployeeMap;
Similar to sequences, Slice dictionaries map to type definitions for NSDictionary and
NSMutableDictionary, with the names
<module‑prefix><Slice‑name> and
<module‑prefix>Mutable<Slice‑name>.
EXMutableEmployeeMap *em = [EXMutableEmployeeMap dictionary];
EXEmployee *e = [EXEmployee employee];
e.number = 42;
e.firstName = @"Stan";
e.lastName = @"Lippman";
[em setObject:e forKey:[NSNumber numberWithLong:e.number]];
e = [EXEmployee employee];
e.number = 77;
e.firstName = @"Herb";
e.lastName = @"Sutter";
[em setObject:e forKey:[NSNumber numberWithLong:e.number]];
To put a value type into a dictionary (either as the key or the value), you must use NSNumber as the object to hold the value. If you have a dictionary that uses a Slice enumeration as the key or the value, you must insert the enumerator as an
NSNumber that holds an
int.
As a convenience feature, the Objective‑C mapping also allows you to insert
NSNull as the value of a dictionary if the value type of the dictionary is a string, structure, sequence, or dictionary. If you send such a dictionary to a receiver, the Ice run time marshals an empty string, default-initialized structure, empty sequence, or empty dictionary as the corresponding value to the receiver, respectively.