0

I have a class RemoteImageLoader which has a method loadImage:

- (void) loadImage: (NSString*) url setTarget:(NSData **)target;

I used a NSData** here because I need to indirectly return NSData* , just like some typical method:

- (BOOL)save:(NSError**)

since the method will actually invoke another asynchronous method, I have to save the target as a member variable so I can access it later. but when I define a NSData ** member variable:

@interface RemoteImageLoader : NSObject    
@property NSData** target;
@end

the compiler complains that "Pointer to non-const type 'NSData*' with no explicit ownership". I've done some search on google, but find no answer of it. Could anyone help me with this? Thanks a lot

and I've tried to replace the declaration to

@interface RemoteImageLoader : NSObject    
@property NSData * __autoreleasing * target;
@end

but the problem still exists

Kazuki Sakamoto
  • 13,929
  • 2
  • 34
  • 96
  • possible duplicate of [Automatic Reference Counting: Pointer to non-const type 'NSError *' with no explicit ownership](http://stackoverflow.com/questions/7804435/automatic-reference-counting-pointer-to-non-const-type-nserror-with-no-expl). Same basic logic. oh, and SO has a search bar too :) – CodaFi May 09 '12 at 03:27
  • Check the ARC transition document. I believe yours is a case similar to NSError. – Jon Shier May 09 '12 at 03:28
  • You _don't_ need to indirectly return that `NSData`. The only reason to do an indirect return is that you also need a _direct_ return value. The `NSError **` parameter technique is used when the method returns a success/failure `BOOL` or an object/`nil` on failure. Your method isn't using its return for anything, so you should just return the new `NSData`; this will make everyone's life simpler (yours and client coders). – jscs May 14 '12 at 00:07

4 Answers4

1

I can't be sure what you are trying to do without seeing your code, but why are you trying to create a pointer to an NSData object (Which is a pointer to NSData). Because you are creating a pointer to a pointer which is probably why you are getting the error. Try removing one of the pointers and see what happens.

Charles Menguy
  • 40,830
  • 17
  • 95
  • 117
Austin Cherry
  • 112
  • 2
  • 10
1

The ARC Transition Notes recommend you declare indirect pointers to NSData and NSError objects in your case with __autoreleasing, like (NSData * __autoreleasing *)target;

__autoreleasing is known as a lifetime qualifier which tells the compiler to return an autoreleased instance of the object.

Because of this, a rewrite of your method signature is required.

- (void) loadImage: (NSString*) url setTarget:(NSData* __autoreleasing *)target;

Be warned, __autoreleasing objects are extremely short lived. Declare your NSData** as __strong to override the default __autoreleasing for a longer lifetime.

CodaFi
  • 43,043
  • 8
  • 107
  • 153
  • Thanks, @CodaFi, in fact I've found the NSError* example before and tried the solution but the problem still exist. the signature of the method loadImage is Ok with both NSData** or (NSData* __autoreleasing *). But for the member variable, neither is successfully compiled. – shicong zhao May 09 '12 at 03:51
  • This answer addresses the exact technical issue that the OP is facing, at the expense of ignoring the fact that what e's trying to do a bad idea for at least two reasons. – jscs May 14 '12 at 00:12
1

I think your method signature is going to cause trouble. A caller is very likely to assume that the pointed-to pointer is filled in as soon as your method returns. Similarly, they are very likely to pass the address of a stack variable which won't be valid for long. Lastly, your method provides no means for the caller to know when the pointed-to pointer has been filled with a value.

You are probably better off taking a completion block from the caller. The completion block will receive an NSData pointer as an argument. Something like:

- (void) loadImage: (NSString*) url completionHandler:(void (^)(NSData* data))block;

This also mirrors the underlying framework API I presume you're using, which is always good for reducing "impedance mismatch".

As for the specific narrow issue you're encountering from the compiler, I suspect the issue is that the compiler can't know if it should emit retains and releases when you assign to *target. It wants you to explicitly declare the ownership characteristic of the pointed-to pointer. I can't check at the moment, but I guess that declaring it as __strong NSData** target would work. That is, it's not interested in whether target owns what it's pointing at, since one can't own a pointer. It's interested in whether the NSData* pointer to which target points owns the NSData object to which it points.

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • Wow, yeah, asynchronously passing a value back by reference...that's one of the worst ideas I've heard all week. – jscs May 14 '12 at 00:10
0

Generally, when you do this sort of thing outside of ARC you do something like:

NSData* returnedParm;
[someObj doSomething:&returnedParm];

on the caller's side. I don't see your equivalent of my returnedParm above. (I've never tried this with ARC, but I'd think the basics would have to be similar.)

Declaring a property as NSData** is declaring a pointer to a non-object and it would not be retained (because there's no object to retain).

My guess is that you should prototype your function as:

-(void)doSomething:(NSData* __autoreleasing*)theReturnParm

and assign to it inside that function using *theReturnParm = something;.

Then on the calling side you'd have your returnedParm as an autoreleased value (so if you want to preserve it you should relatively quickly assign it to a strong pointer).

Hot Licks
  • 47,103
  • 17
  • 93
  • 151