Question

In Objective C, I have an object e.g. Person with a lot of fields firstName, lastName, phoneNumber, address, city... and so on. These fields types are NSString and any of these could be nil.

Now I want to concatenate my field values in another NSString :

Person *p = ...
NSMutableString *s = [[NSMutableString alloc] init];
for (NSString *field in @[p.firstName, p.lastName, p.phoneNumber,
                          p.adress, p.city, ....more fields...]) {
    if ([field length] > 0) {
        [s appendFormat:@"%@\n", field];
    }
}

Issue is that this code crash whenever one of the field is nil. I have the exception :

[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object 
from objects[0]'

How could I handle simply the case of nil values within my for loop ?

Was it helpful?

Solution

I agree with @TomPace's post, for this small number I would do a simple if/else.

However, there may be times you do need to loop through a list of fields.

It's a bad idea to blindly pull the values into an array as you could be trying inserting nil values into the array. In this case, it would be better to place the field names into a key array as strings and loop through the list using valueForKey: to access the values. I would possibly store the keys list somewhere else where it can be used again.

Person *p = ...
NSMutableString *s = [[NSMutableString alloc] init];

NSArray *keys = @[@"firstName", @"lastName", @"phoneNumber", @"adress", @"city"];

for (NSString *key in keys) 
{
    NSString *value = [p valueForKey:key];
    if ([value length] > 0) {
        [s appendFormat:@"%@\n", value];
    }
}

OTHER TIPS

Person *person = [[Person alloc] init];
person.firstName = nil;
person.lastName = @"lastName";

NSMutableString *s = [[NSMutableString alloc] init];
[s appendFormat:@"%@\n", person.firstName == nil?@"":person.firstName];
[s appendFormat:@"%@\n",  person.lastName == nil?@"":person.lastName];

For a selection of fields this small, don't use a for loop.

You may be saving a bit of code by attempting the for-loop structure, but it's really not the way to go if you're building the NSArray with only a few fields, and especially because you can't put nil items in it.

A better way to go is:

Person *p = ...
NSMutableString *s = [[NSMutableString alloc] init];
if ([p.firstName length] > 0)   [s appendFormat:@"%@\n", p.firstName];
if ([p.lastName length] > 0)    [s appendFormat:@"%@\n", p.lastName];
if ([p.phoneNumber length] > 0) [s appendFormat:@"%@\n", p.phoneNumber];
if ([p.adress length] > 0)      [s appendFormat:@"%@\n", p.adress];
if ([p.city length] > 0)        [s appendFormat:@"%@\n", p.city];

Edit, after original Question was updated with large amount of fields. Like @BergQuester said, an approach to support a larger, arbitrary set of fields is using KVO-style inspection.

NSArray *fieldNames = @[@"firstName", @"lastName", @"phoneNumber", ....more fields...];
NSString *field;
for (NSString *fieldName in fieldNames) {
    field = [p valueForKey:fieldName];
    if ([field length] > 0 ) {
        [s appendFormat: @"%@\n", field];
    }
}

Try to create NSMutableString category

#import "NSMutableString+checkForNilObject.h"

@implementation NSMutableString (checkForNilObject)


-(void) appendNotNillObject:(NSString *) string
{
    if(string)
    {
       [self appendString:string];
    }
}
@end

You can override the getters of the class Person.

    @implementation Person

    - (NSString *)firstName{
        if (_firseName == nil)
             _firstName = @"";
        return _firstName;
    }
///....Other setters    

    @end

Like this you can define all your setters here.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top