3
votes

I want to pass 2 variables:

UIImage * img
int i

into another method that only takes a (void *)

I tried making a C struct containing both img and i

struct MyStruct {
    UIImage *img;
    int i;
}

but xcode gives me an error saying "ARC forbids Objective-C objects in structs or unions"

The next thing I tried is to write an objective-c class MyStruct2 containing img and i, alloc-initing an instance of it and typecasting it as (__bridge void*) before passing it to the method. Seems little involved for my use case. Seems like there should be a better way.

What's the simplest way to achieve this?

Thank you.

Edit based on comments: I have to use void * as it is required by the UIView API. I created a selector as mentioned by UIVIew API

+ (void)setAnimationDidStopSelector:(SEL)selector

Please see documentation for setAnimationDidStopSelector at http://developer.apple.com/library/ios/#documentation/uikit/reference/UIView_Class/UIView/UIView.html . It says ... The selector should be of the form:

    - (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context 

I want to pass both img and i into the (void *)context argument.

2
Why not change the method you are passing them to?Evan Mulawski
Just out of curiosity, why not changing the method itself?Anne
@Anne maybe he's using pthreads and the callback can take 1 param only.user529758

2 Answers

4
votes

You can do this:

struct MyStruct {
    __unsafe_unretained UIImage *img;
    int i;
}

However, if you do this, you must keep in mind that the img field will not automatically retain and release its UIImage.

If you post more details about your “method that only takes a (void *)”, we might be able to give you better advice.

EDIT

You have updated your question to say that you need to pass a void * to the animationDidStop:finished:context: selector. Since you're using ARC, I know you're targetting iOS 4.0 or later, so you should just use animateWithDuration:animations:completion: instead. Then you don't need to pass a void *.

UIImage *img = someImage();
int i = someInt();

[UIView animateWithDuration:2.0 animations:^{
    // set the animatable properties here
} completion:^(BOOL finished) {
    doSomethingWithImageAndInteger(img, i);
}];
3
votes

ARC cannot follow ObjC objects in plain old C structs. It tracks pointers on the stack, or in ObjC and C++ objects, which are copied using methods, explicit or implicit, but under the compiler control anyway.

So... use a class. C++ if you like it, or :

@interface MyNotSoPlainStruct

@property ( nonatomic, strong ) UImage* image;
@property ( nonatomic ) int i;

@end

@implementation MyNotSoPlainStruct
@end

MyNotSoPlainStruct* foo = [MyNotSoPlainStruct new];
foo.image = …
foo.i = …

and there you go.

The solution needs more lines, that's true, but you have a much better control on how things are done, including the image life cycle. You can use a strong pointer, or a weak one if it suits your needs better.

Of course, you can also use either a dictionary or an array. Literals make that especially easy:

NSDictionary* arguments = @{ @"image" : <image>, @"i" : @( <int> ) };
arguments[@"i"].intValue returns the int you need.

Pretty easy.

edit

Forgot the transfer part, oops :). So then:

You must use the following annotations:

void* argumentsPointer = (__bridge_retained void*) arguments;

arguments is retained, and that reference is no longer managed by ARC.

When you receiver your pointer back, you must convert it:

NSDictionary* arguments = (__bridge_transfer NSDictionary*) argumentsPointer;

Using __bridge only may result in a dangling pointer if the last known reference disappear. If you want that void* to retain the dictionary, you must use __bridge_retained first, then __bridge_transfer.