@Ilija: If you post to say you've let it go, then you haven't really let it go yet. ;) Let me see if I can clarify my comment:
-(NSString *) album
{
return self->album;
}
-(NSDictionary *) albumAndArtist
{
return @{ @"album":self.album, @"artist":self.artist };
}
The album
method above is what the Objective-C compiler will generate for you automatically.* The albumAndArtist
method is what I suggested in my answer to your original question. Now, if you ask Clang to lower these two methods to C (clang -rewrite-objc test.m -o test.cc
), you get something like this:
static NSDictionary * _I_iTunesTrack_albumAndArtist_albumAndArtist(iTunesTrack * self, SEL _cmd) {
return ((NSDictionary *(*)(id, SEL, const id *, const id *, NSUInteger))(void *)
objc_msgSend)(objc_getClass("NSDictionary"), sel_registerName("dictionaryWithObjects:forKeys:count:"),
(const id *)__NSContainer_literal(2U, ((NSString *(*)(id, SEL))(void *)objc_msgSend)
((id)self, sel_registerName("album")), ((NSString *(*)(id, SEL))(void *)objc_msgSend)
((id)self, sel_registerName("artist"))).arr, (const id *)__NSContainer_literal(2U,
(NSString *)&__NSConstantStringImpl_test_m_0, (NSString *)&__NSConstantStringImpl_test_m_1).arr, 2U);
}
or, in human terms,
-(NSDictionary *) albumAndArtist
{
id album = objc_msgSend(self, sel_registerName("album"));
id artist = objc_msgSend(self, sel_registerName("artist"));
id *values = calloc(2, sizeof(id)); values[0] = album; values[1] = artist;
id *keys = calloc(2, sizeof(id)); keys[0] = @"album"; keys[1] = @"artist";
Class dict_class = objc_getClass("NSDictionary");
id result = objc_msgSend(dict_class, sel_registerName("dictionaryWithObjects:forKeys:count:"), values, keys, 2);
free(values); free(keys);
return result;
}
Check it out: three sel_registerName
s, one objc_getClass
, three objc_msgSend
s, two calloc
s, and two free
s. That's pretty inefficient, compared to the compiler-generated album
method.
Technically, the compiler-generated album
method looks like this:
-(NSString *) album
{
return objc_getProperty(self, _cmd,
__OFFSETOFIVAR__(struct iTunesTrack, album), /*atomic*/ YES);
}
but that's only because it wasn't originally declared as nonatomic
. For the meaning of objc_getProperty
, see here; but basically, it's faster than an objc_msgSend
would have been.)
So clearly albumAndArtist
is going to be much slower than album
, because of all that extra work it's doing. But — you ask — what if we get rid of all that work and just return self.album
? Well, the generated code is still hairier than what the compiler generated for the album
getter:
-(NSString *) albumAndArtist_stripped_down
{
// return self.album;
return objc_msgSend(self, sel_registerName("album"));
}
When your program calls myTrack.album
, it invokes objc_msgSend
once to figure out that it should call the album
method, and then inside album
it invokes objc_getProperty
. That's two calls. (Three if you count selector_registerName("album")
.)
When your program calls myTrack.albumAndArtist_stripped_down
, it invokes objc_msgSend
once to figure out that it should call the albumAndArtist_stripped_down
method, and then that calls objc_msgSend
a second time, and then that calls objc_getProperty
. That's three calls. (Five if you count selector_registerName
.)
So it makes sense to me that albumAndArtist_stripped_down
should be about twice as slow (or 5/3 as slow) as album
by itself.
And as for the original albumAndArtist
, just by counting the function calls, I'd expect it to be about five times as slow as album
... but of course it'll be much slower than that, because it's doing at least three memory allocations, whereas album
isn't doing any. Memory allocation and cleanup is very expensive, because malloc
is a complicated algorithm in its own right.
I hope this clears up the issue for you. :)