Reputation: 12382
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.
Upvotes: 3
Views: 950
Reputation: 5558
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.
Upvotes: 3
Reputation: 385860
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.
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);
}];
Upvotes: 4