0

According to the OpenGL ES Programming guide's section on texturetool,

Your app must parse the data header to obtain the actual texture data. See the PVRTextureLoader sample for an example of working with texture data in the PVR format.

This is all well and good, but 3 years later, and PVRTextureLoader's PVRTexture.m doesn't compile because it needs to be converted in order to work in an ARC project. I reckon I could flag these two files as non-ARC but I wanted to at least learn a little about Objective-C this time around.

Here's a bit of code that's giving me trouble at this point:

+ (id)pvrTextureWithContentsOfFile:(NSString *)path
{
    return [self initWithContentsOfFile:path];
}

This was manually converted from:

+ (id)pvrTextureWithContentsOfFile:(NSString *)path
{
    return [[[self alloc] initWithContentsOfFile:path] autorelease];
}

Maybe someone could be so kind as to walk through what this actually does (as it makes no sense to me to refer to self from what is clearly declared as a class method and not an instance method), but the actual error seen is

<...>/PVRTexture.m:256:15: error: no known class method for selector 'initWithContentsOfFile:'
        return [self initWithContentsOfFile:path];
                     ^~~~~~~~~~~~~~~~~~~~~~
1 error generated.

As for why I am manually converting this file to ARC, the Edit->Refactor->Convert to Objective-C ARC... menu option basically says "fix these 8 errors before I can continue" and these 8 errors are of course ARC-related errors. Which I was hoping the conversion would be able to resolve. Circular dependencies are only fun the first time around.

The curious bit is that -initWithContentsOfFile:path is right there in the file too:

- (id)initWithContentsOfFile:(NSString *)path
{
    if (self = [super init])
    {
        NSData *data = [NSData dataWithContentsOfFile:path];

        _imageData = [[NSMutableArray alloc] initWithCapacity:10];

        _name = 0;
        _width = _height = 0;
        _internalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
        _hasAlpha = FALSE;

        if (!data || ![self unpackPVRData:data] || ![self createGLTexture])
        {
            self = nil;
        }
    }

    return self;
}

Note: This code compiles if I change the + to a - on the pvrTextureWithContentsOfFile declaration. I am positive the original code had a + there, so please, somebody help explain this to me.

Steven Lu
  • 41,389
  • 58
  • 210
  • 364

2 Answers2

1

I got it, I think. Here's another example where writing out the question leads to the answer.

It's this:

return [[self alloc] initWithContentsOfFile:path];

I'm sure Google will be happy enough to (let SO) show me what alloc actually does. I guess calling [self alloc] from a class method is essentially what makes that method into a factory method that can generate instances of that class. This is nothing other than the Obj-C way to new something.

Me, I made the mistake of assuming alloc was something that ARC abolished.

Also, a side note: Reading carefully helps. I also just found out about GLKTextureLoader so I didn't need to convert PVRTextureLoader at all.

Steven Lu
  • 41,389
  • 58
  • 210
  • 364
  • Nope, `release`, `retain`, `autorelease` and calling `dealloc` on `super` are all abolished. `alloc` survives since there'd be no other way to do what it does — get a new instance from a class. – Tommy Dec 03 '13 at 01:37
  • Well it could be [`new`ed](http://stackoverflow.com/q/719877/340947), eh? (though, not what I would want to use in this situation) – Steven Lu Dec 03 '13 at 01:39
  • Technically yes, since `NSObject` isn't necessarily compiled with ARC so can call any other abolished method it likes (and you could even write a non-ARC category on `NSObject` with a method called `releaseSecretly` that just called `release` and call that from ARC code if you really wanted to break your program), but `new` is a bit useless so it'd be odd if things had gone that way. I don't think it's anything more than a Smalltalk holdover. – Tommy Dec 03 '13 at 01:42
  • I'm now starting to worry about `[self release]; self = nil;` being converted to `self = nil;` becoming a bug (the `initWithContentsOfFile` function above). Will `self = nil` be doing something subtly wrong, or is it still correctly dealloc'ing the instnace on account of it being an init-method? (cross-reference: Second comment to [this answer](http://stackoverflow.com/a/6768136/340947)) also thanks for sticking around to comment. – Steven Lu Dec 03 '13 at 01:48
1

alloc is a class method. You usually see alloc used from another class, with things like:

NSView *myView = [[NSView alloc] initWithFrame: someRect];

In a class method, self is the class, not an instance of that class. So [self alloc] allocates an instance of the class. It would also be valid to use the name of the class explicitly, so in the case of your PVRTexture class,

return [[self alloc] initWithContentsOfFile:path];

Could be replaced with

return [[PVRTexture alloc] initWithContentsOfFile:path];

Both are perfectly valid. I would probably use the second form, just because, like you, I find the first form a little odd-looking.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • Curiously enough this sample code isn't even consistent in this regard, and the next method which is `pvrTextureWithContentsOfURL` ends with `return [PVRTexture pvrTextureWithContentsOfFile:[url path]];` – Steven Lu Dec 03 '13 at 01:53