0

How would I turn this:

xmlObject *system_domain  = [xmlObject alloc] 
xmlObject *system_description = [xmlObject alloc]
xmlObject *system_type = [xmlObject alloc]

Into something where I would only have to write xmlObject and [xmlObject alloc] once.

xmlObject *system_domain, *system_description, *system_type = [xmlObject alloc]

This makes all of them xmlObjects, but does not allocate each one. Any help would be much appreciated.

John
  • 3,037
  • 8
  • 36
  • 68
  • 2
    Don't forget to `-init` them all too... – Dave DeLong Oct 02 '12 at 17:54
  • `-init` initializes objects, if you want to use it you need to initialize it :p – ohr Oct 02 '12 at 19:23
  • I thought -init wasn't necessary when using ARC with Objective C – John Oct 02 '12 at 19:30
  • ARC has absolutely nothing to do with `init`. It's about memory management. – jscs Oct 02 '12 at 21:19
  • Ideally, the classes you deal with will have a class constructor sugar method, so you can just call `[Foo foo]`, which will `alloc`, `init`, and do "something unspecified" to take care of memory management (basically meaning `autorelease` without ARC or GC, nothing with ARC or GC), all in one call, so you don't have to worry about this. But you'll always run into some classes that don't have this, so you need to learn the `alloc] init]` idiom (and, if you ever need to deal with code without ARC or GC, `alloc] init] autorelease]` too). – abarnert Oct 03 '12 at 18:44

2 Answers2

2

If you've got a set of related objects, and being able to iterating over them is more important than being able to access them concisely, you should stick them in a collection:

NSMutableDictionary *system_info = [NSMutableDictionary dictionary];
for (id key in @[@"domain", @"description", @"type"]) {
  system_info[key] = [xmlObject alloc];
}

Then, instead of system_dictionary you use system_info[@"dictionary"]. (You could even use KVC to make things more concise, if that's important.) Of course if that doesn't fit your use case, it would be silly to stick them in a dictionary in the first place.

In any other use case, what you've done is the normal, idiomatic Objective C way to declare three xmlObjects. If you really want to know if there are ways around it, of course there are, but most of them are pretty silly.

A better solution might be to switch languages. Python, Ruby, Applescript, ObjC++, eero, etc. all let you access the ObjC runtime just as easily as ObjC, and have more concise idiomatic ways of doing things. For example, in Python:

system_domain = xmlObject.alloc()
system_description = xmlObject.alloc()
system_type = xmlObject.alloc()

Or even:

system_domain, system_description, system_type = [xmlObject.alloc() for _ in range(3)]

Another reasonable option, if you have to initialize 500 of these things in a row, is to write some simple code that generates your ObjC code.

But if you really want to stay in ObjC, here are some of the silly solutions:

You can cut the number of xmlObject appearances from 6 to 4 just by doing this:

xmlObject *system_domain = [xmlObject alloc],
     *system_description = [xmlObject alloc],
            *system_type = [xmlObject alloc];

Or to 3:

id system_domain = [xmlObject alloc];
id system_description = [xmlObject alloc];
id system_type = [xmlObject alloc];

Or to 1:

Class x = xmlObject;
id system_domain = [x alloc];
id system_description = [x alloc];
id system_type = [x alloc];

Or:

id makeXmlObject() { return [xmlObject alloc]; }
...

id system_domain = makeXmlObject();
id system_description = makeXmlObject();
id system_type = makeXmlObject();

A few side notes:

You probably don't want to use the result of [xmlObject alloc]. That's a block of enough memory to construct an xmlObject, connected to the xmlObject class, but otherwise completely uninitialized. You have to call an initializer—typically -[init], but often something like -[initWithValue: andOtherValue:]—before you can do anything useful with it.

So, most idiomatic ObjC code will be full of calls like this:

Foo *foo = [[Foo alloc] init];
Bar *bar = [[Bar alloc] initWithFoo:foo];

Also, unless you're using ARC (or GC), you usually want to autorelease the object as soon as it's initialized; otherwise, you have to manage the memory manually (and that's as hard to do properly as it is to say 10 times fast). So, if you ever have to deal with non-ARC code, you'll see this:

Bar *bar = [[[Bar alloc] initWithFoo:foo] autorelease];

Fortunately, many classes provide class constructors that do everything in one:

NSString *s = [NSString stringWithUTF8String:c]; // ARC or no ARC

You should use these convenience methods when they exist, but get used to the alloc] init]; (and alloc] init] autorelease];, if you ever have to deal with pre-ARC/pre-GC code) idiom, because you will need it.

The same is true in all other languages with ObjC runtime bindings; e.g., in Python, you do Bar.barWithFoo(foo) when possible, Bar.alloc().initWithFoo_(foo) otherwise.

Meanwhile, the reason you can't something like this:

xmlObject *system_domain, *system_description, *system_type = [xmlObject alloc];

… or …

system_domain = system_description = system_type = [xmlObject alloc];

… is that the only reasonable interpretation of this would be to set all three objects to the same instance of xmlObject. If you only call alloc once, only one thing gets allocated.

Finally, it's considered bad style to name ObjC classes with a lowercase first letter; it should be XMLObject (or maybe XmlObject, but Apple likes to make acronyms and initialisms explicit), not xmlObject. And, except in very simple projects, most people like to give all of their classes a 2- or 3-letter prefix (like Apple's NS), to make it possible to distinguish classes coming from different subprojects, third-party libraries, etc., so 'ALIXMLObject` might be even better.

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • Just a note, it's basically unheard of in Cocoa, though technically valid, to use an `alloc`'d but not `init`'d object. All of those `[xmlObject alloc]` lines should really be `[[xmlObject alloc] init]`. – jscs Oct 02 '12 at 19:17
  • That's already noted in the comments to the original question. Do you think it needs to be in the answer as well? I didn't want to get into it, because then you also probably have to talk about using `autorelease` or manually releasing unless you're using ARC, or, if the class provides a `+[xmlObject]` method, just use that instead of `+[alloc]` and `-[init]`… – abarnert Oct 03 '12 at 18:24
0

You'd have to do something like:

xmlObject *system_domain = [xmlObject alloc],
     *system_description = [xmlObject alloc],
            *system_type = [xmlObject alloc];

Which is hardly better than your first version.

Mat
  • 202,337
  • 40
  • 393
  • 406