C function calling objective C functions - objective-c

I've a c function in my viewController.m.
int abc(int a, char* b)
{
//do something
}
I also have a function
-(void) callIncomingClass
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
//set the position of the button
button.frame = CGRectMake(100, 170, 100, 30);
//set the button's title
[button setTitle:#"Click Me!" forState:UIControlStateNormal];
//add the button to the view
[self.view addSubview:button];
}
Now I want to call callIncomingClass from within the function abc now.
How do you suggest I go about it??
Why I want to call an Objective C method from the C function is, I've cannot create a button or do some processing like that in the C function.
Should the following code work :
int abc(int a, char* b)
{
ViewController * tempObj = [[ViewController alloc] init];
[tempObj callIncomingClass];
}
edit : the big picture of what I am doing
There is a c library, i.e. a library.c and library.h file. The library.h file has a struct that has callback functions. These need to be assigned with function pointers. so I've a c function with the signature int abc(int,char*) that is to be assigned to the callback function in the struct.
This function abc is defined in ViewController.m.
Ideally i wanted it to be defined in a separate file. but this is also okie.
So now, the callback event happens, I want to create a UIButton with some action on the View. As I can't create a UIButton from a c function, i am calling a objective C method of the ViewController class, that creates the UIButton.
Hope that clears the picture as to how I am planning to use this.

Your button doesn't show because of what others and myself were saying: you need the existing instance of the ViewController. You are creating an entirely new instance of the ViewController, which is never brought on screen or pushed, etc.
You can accomplish what you need to do by using a global variable that points to your existing instance.
Here's what your .m should look like:
#import "ViewController.h"
static ViewController *viewController = nil;
#implementation ViewController
- (id)init {
if ((self = [super init])) {
viewController = self;
}
return self;
}
- (void)dealloc {
viewController = nil;
}
-(void) callIncomingCreateButton {
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
//set the position of the button
button.frame = CGRectMake(100, 170, 100, 30);
//set the button's title
[button setTitle:#"Click Me!" forState:UIControlStateNormal];
//add the button to the view
[self.view addSubview:button];
}
- (IBAction)DemoCall:(id)sender {
callIncoming(1, "a");
}
#end
int callIncoming(int a, char* b) {
[viewController callIncomingCreateButton];
return a;
}

You need to get access to the correct ViewController* / instance, not any old one. If your C function signature doesn't let you pass in some arbitrary data via a void* or similar, then you need to use a class method, and a static variable to hold the temporary pointer as follows.
In your Objective C file:
static ViewController* cLibReceiver = NULL;
+(void) callIncomingClassOuter {
[cLibReceiver callIncomingClass];
}
-(void) beforeTriggerCLibrary {
cLibReceiver = self;
}
and in your (different) Objective C file with abc() in:
int abc(int a, char* b)
{
[ViewController callIncomingClassOuter];
}
Where you trigger the C Library, you need to do this:
[theViewController beforeTriggerCLibrary]; // could be self, depending on context
c_library_call();
// optionally set cLibReciever back to NULL.
note: I may have got some of the syntax wrong on method headers etc, I'm not entirely familiar with objective C calling conventions. class methods are definitely + though.
note 2: you may not be allowed to extend the system class like this - you may need to define your own subclass. Again, not familiar enough with Objective C.

Assuming that you are in an Objective-C source file, calling an Objective-C function from a C function works exactly the same as calling the Objective-C function from any other Objective-C function. In either case, if you have a pointer ptr to the object that you want to call the function on, you write
[ptr callIncomingClass];
so of course in either case you need to somehow have the pointer to the object you want to call the function on. If you are in an Objective-C function (i.a. an instance method) the usual "source" for such pointers are (a) the implicit "self" pointer when you are calling a method on the same object as the currently running methid, (b) some instance-variable of the object on which the currently running method was called, (c) an argument to the currently running function or method, (d) a global variable or the result of some other function call. In plain C you can use (c) and (d), but not (a) and (b) because you don't have a self.

Related

How to replace pickerView:numberOfRowsInComponent method for 1 instance of pickerView

I want to create a pickerView programmatically and have it use it's own version of a method like pickerView:numberOfRowsInComponent.
I create the instance at runtime like this:
UIPickerView *myPickerView = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 200, 320, 200)];
myPickerView.delegate = self;
myPickerView.dataSource = self;
myPickerView.showsSelectionIndicator = YES;
[self.view addSubview:myPickerView];
The standard method called would be:
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
NSUInteger numRows = 5;
return numRows;
}
What I want to do is replace this standard method with another method for this instance only.
-(NSInteger)xxxpickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{ // special method for this instance only
return 1;
}
I've been able to use method swizzle to do this with other things, but I can't seem to get it to work with UIPickerView.
#implementation UIPickerView (Tracking)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = #selector(pickerView:numberOfRowsInComponent:);
SEL swizzledSelector = #selector(xxxpickerView:numberOfRowsInComponent:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
I've listed the methods to see if the 2nd method was added to the instance during runtime and it is in the list.
However, the 2nd method doesn't run, the 1st method does run.
Here's a link to the post that got me started on this, and I've confirmed it works, but I seem to be missing something about this.
http://nshipster.com/method-swizzling/
I'm open to other suggestions, the problem I'm trying to solve is that I want to create the instance of a UIPickerView object that won't be dependent on another instance that will be running at the same time. So I want a different method that will work only with the one instance and completely ignore any other instances that might be running and I want to do this programmatically.
At lest one reason for not using a tag/switch, is that I don't know what the conditions will be until runtime.
I don't know why swizzle would work with one object and not another, and I'm open to other way to replace stock methods with others at runtime.
Is there something about the method I'm trying to replace that won't allow it to be replaced?
EDIT: in order to try and make the question clear, the following code in the link works. It swaps one method for another method. What I need to do is the same thing for another object and I can't figure out what it works for 1 object and not another.
This works for another object: http://nshipster.com/method-swizzling/
Here's another link as well: http://blog.newrelic.com/2014/04/16/right-way-to-swizzle/
One simple way to do it is keep a copy of the pointer in a property and then compare pointers in the datasource/delegate methods.
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
if ( pickerView == self.myPickerView )
return 1; // response for this particular picker view
else
return 5; // standard response
}

Objective-C - Storing block with parameters for callback

I have a general routine, which takes a few parameters.
Something like:
-(id) doStuff:(int)A:(int)B:(int)C {
//doStuff
return object;
}
I have a UITableViewController, which houses a number of custom cells, each with their own ID. When 'Save' is hit, these cells are iterated and some cells need 'additional behaviour' when they are being saved.
Up to now, I've created a 'Callback' object, which stores an NSString * and a delegate in the custom cell. Upon being 'Saved', the cell looks, whether it has any callbacks to apply and uses
SEL sel = NSSelectorFromString(Sel);
if([Del respondsToSelector:sel])
[Del performSelector:sel withObject:Cell];
Now that works somewhat well..., however, it requires the method I pass to do a switch/case on the ID of the Cell that's passed, and I'd like to avoid that.
That's why I'd like to use blocks instead, but I don't really know how to store a parameterized block in a variable.
What I'm trying to do:
Declare a function block doStuff.
id (^doStuff) (int, int, int) = ^(int A, int B, int C) {
//does Stuff
};
And add the previously created block as callback
[Cell addCallback:(^doStuff)(1, 2, 3)];
The block must NOT be called at that moment, but stored in the cell and only called it when the time is right.
How would I go about this correctly?
Thank you very much.
Edit: What I'd also like to avoid is storing the parameters for the block in the cell and pass them upon calling, because that would require me to further specialize the cells unnecessarily.
It sounds like what you want is a block that calls your block, something like this:
[cell addCallback:^{ doStuff(1, 2, 3); }];
But this is a rather odd and convoluted design. It seems like there is probably a way to write it with only one block, but it's hard to give a solution that specific without a better idea of what you're doing.
The most straight forward way is to create a typedef containing how the block parameters should look like, then use it to declare a new property/ivar. The following sample code is copied from the Sensible TableView framework SCCellActions class:
typedef void(^SCCellAction_Block)(SCTableViewCell *cell, NSIndexPath *indexPath);
#interface SCCellActions : NSObject
...
#property (nonatomic, copy) SCCellAction_Block willDisplay;
...
#end
You could then set the property as follows:
cellActions.willDisplay = ^(SCTableViewCell *cell, NSIndexPath *indexPath)
{
cell.backgroundColor = [UIColor yellowColor];
};
Similarly, you could declare a parameter as follows:
...
- (void)callActionBlock:(SCCellAction_Block actionBlock)
{
if(actionBlock)
{
actionBlock(self.cell, self.cellIndexPath);
}
}
...
In which case the method should be called like this:
[myObject callActionBlock:^(SCTableViewCell *cell, NSIndexPath *indexPath {cell.backgroundColor = [UIColor yellowColor];}];
This answer is based on Chuck's suggestion and describes the pitfalls I encountered realizing it.
Creation:
Cell = [self CreateCell];
[Cell addCallback:^{ return doStuff(Cell, 1, 2, 3, 4) } At:ON_SAVE];
doStuff is a local block, declared before the cells. I was unable to add it directly to the cell, because I also needed a reference to the calling cell within the block.
Pitfall at this point: Class variables.
A block will only retain...or rather 'copy'...local variables, but not class variables.
Assuming that 'Cell' was a class variable and set by 'CreateCell', the block would work with the value of Cell at the time the block is executed.
As such, it is important to remember to declare a local variable, which assumes the value of the class variable if necessary.
Storage:
- (void) addCallback:(CallBlock_t)B At:(int)at {
//Creates a Callback-Object and passes it the block and adds it to an Array.
}
- (id) initWithBlock:(CallBlock_t)B At:(int)at {
self = [super init];
if(self) {
Block = [B copy]; //Yes, Copy. Not retain.
When = at;
}
return self;
}
Pitfall at this point: If the block is merely retained, the local block from the calling function will go out of scope and the program will fail with 'Bad Access'. Copy resolves this problem.
Of course you need to release the Block once you're done using it (in the dealloc of the callback class), but that's a given.
I hope this little explanation will save someone some grief.

Setting/getting global variables in objective-C

I am writing an app which is a sort of dictionary - it presents the user with a list of terms, and when clicked on, pops up a dialog box containing the definition. The definition itself may also contain terms, which in turn the user can click on to launch another definition popup.
My main app is stored in 'myViewController.m'. It calls a custom UIView class, 'CustomUIView.m' to display the definition (this is the dialog box that pops up). This all works fine.
The text links from the CustomUIView then should be able to launch more definitions. When text is tapped in my CustomUIView, it launches another CustomUIView. The problem is, that this new CustomUIView doesn't have access to the hash map which contains all my dictionary's terms and definitions; this is only available to my main app, 'myViewController.m'.
Somehow, I need to make my hash map, dictionaryHashMap, visible to every instance of the CustomUIView class. dictionaryHashMap is created in myViewController.m when the app opens and doesn't change thereafter.
I don't wish to limit the number of CustomUIViews that can be opened at the same time (I have my reasons for doing this!), so it would be a little resource intensive to send a copy of the dictionaryHashMap to every instance of the CustomUIView. Presumably, the solution is to make dictionaryHashMap a global variable.
Some of my code:
From myViewController.m:
- (void)viewDidLoad
{
self.dictionaryHashMap = [[NSMutableDictionary alloc] init]; // initialise the dictionary hash map
//... {Code to populate dictionaryHashMap}
}
// Method to pop up a definition dialog
- (void)displayDefinition:(NSString *) term
{
NSArray* definition = [self.dictionaryHashMap objectForKey:term]; // get the definition that corresponds to the term
CustomUIView* definitionPopup = [[[CustomUIView alloc] init] autorelease]; // initialise a custom popup
[definitionPopup setTitle: term];
[definitionPopup setMessage: definition];
[definitionPopup show];
}
// Delegation for sending URL presses in CustomUIView to popupDefinition
#pragma mark - CustomUIViewDelegate
+ (void)termTextClickedOn:(CustomUIView *)customView didSelectTerm:(NSString *)term
{
myViewController *t = [[myViewController alloc] init]; // TODO: This instance has no idea what the NSDictionary is
[t displayDefinition:term];
}
From CustomUIView.m:
// Intercept clicks on links in UIWebView object
- (BOOL)webView: (UIWebView*)webView shouldStartLoadWithRequest: (NSURLRequest*)request navigationType: (UIWebViewNavigationType)navigationType {
if ( navigationType == UIWebViewNavigationTypeLinkClicked ) {
[myViewController termTextClickedOn:self didSelectTerm:request];
return NO;
}
return YES;
}
Any tips on how to make the dictionaryHashMap visible to CustomUIView would be much appreciated.
I have tried making the dictionaryHashMap global by doing the following:
Changing all instances of 'self.dictionaryHashMap' to 'dictionaryHashMap'
Adding the line 'extern NSMutableDictionary *dictionaryHashMap;' to CustomUIView.h
Adding the following outside of my implementation in myViewController.m: 'NSMutableDictionary *dictionaryHashMap = nil;'
However, the dictionaryHashMap remains invisible to CustomUIView. As far as I can tell, it actually remains a variable which is local to myViewController...
It's not resource-intensive to pass around the reference (pointer) to dictionaryHashMap. A pointer to an object is only 4 bytes. You could just pass it from your view controller to your view.
But I don't know why you even need to do that. Your view is sending a message (termTextClickedOn:didSelectTerm:) to the view controller when a term is clicked. And the view controller already has a reference to the dictionary, so it can handle the lookup. Why does the view also need a reference to the dictionary?
Anyway, if you want to make the dictionary a global, it would be more appropriate to initialize it in your app delegate, in application:didFinishLaunchingWithOptions:. You could even make the dictionary be a property of your app delegate and initialize it lazily.
UPDATE
I didn't notice until your comment that termTextClickedOn:didSelectTerm: is a class method. I assumed it was an instance method because myViewController starts with a lower-case letter, and the convention in iOS programming is that classes start with capital letters. (You make it easier to get good help when you follow the conventions!)
Here's what I'd recommend. First, rename myViewController to MyViewController (or better, DefinitionViewController).
Give it a property that references the dictionary. Whatever code creates a new instance of MyViewController is responsible for setting this property.
Give CustomUIView properties for a target and an action:
#property (nonatomic, weak) id target;
#property (nonatomic) SEL action;
Set those properties when you create the view:
- (void)displayDefinition:(NSString *)term {
NSArray* definition = [self.dictionaryHashMap objectForKey:term];
CustomUIView* definitionPopup = [[[CustomUIView alloc] init] autorelease]; // initialise a custom popup
definitionPopup.target = self;
definitionPopup.action = #selector(termWasClicked:);
...
In the view's webView:shouldStartLoadWithRequest: method, extract the term from the URL request and send it to the target/action:
- (BOOL)webView: (UIWebView*)webView shouldStartLoadWithRequest: (NSURLRequest*)request navigationType: (UIWebViewNavigationType)navigationType {
if ( navigationType == UIWebViewNavigationTypeLinkClicked ) {
NSString *term = termForURLRequest(request);
[self.target performSelector:self.action withObject:term];
return NO;
}
return YES;
}
In the view controller's termWasClicked: method, create the new view controller and set its dictionary property:
- (void)termWasClicked:(NSString *)term {
MyViewController *t = [[MyViewController alloc] init];
t.dictionary = self.dictionary;
[t displayDefinition:term];
}
Create a class that will be used as singleton. Example.
You Should always keep your data in separate class as the mvc pattern suggest and that could be achieved by using a singleton class for all your dictionary terms and accesing them from every custom view when needed.

How to name a block of code and call it in a different method?

I use Grand Central Dispatch methods to do some executions of my app in a queue. I decide the frames for buttons in a calculation on that queue. I want my app to re-draw its scren and calculate new frames after rotation. Here is some pseudo code explanation from what i do:
CGFloat a=123, b=24;
dispatch_async(drawingQue, ^{
//needed loops to get the total button count-how many ones will be drawn et..
for(int x=0;x<someCount<x++){
for(int y=0;y<anotherCount;y++){
//needed frame&name ect assingments
button.frame= CGRectMake(x+y, x-y, a, b);
[button setTitle:#"abc"];}}
};
Here what i want is, how can i give this block a name and re-use it in the
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
}
delegate method? For instance, if the rotation is landscape, i want to use a=234 instead of 123.. Any help please. Thanks in advance..
Declare an instance variable of block type and use Block_copy to keep the block:
#interface My {
void (^myBlock)(void);
}
#end
myBlock = Block_copy(^{
...block code...
});
// later call it
myBlock();
// don't forget to release it in dealloc
It is important to copy the block before storing it outside of the scope of its literal (^{...}), because the original block is stored on stack and will die when the scope exits.
Just make a #property that's a block, store it, and use it again later:
typedef void (^MyBlock)(CGFloat, CGFloat);
...
#property(readwrite, copy) MyBlock buttonFramesBlock;
...
#synthesize buttonFramesBlock;
...
self.buttonFramesBlock = ^(CGFloat a, CGFloat b){
//needed loops to get the total button count-how many ones will be drawn et..
for(int x=0;x<someCount<x++){
for(int y=0;y<anotherCount;y++){
//needed frame&name ect assingments
button.frame= CGRectMake(x+y, x-y, a, b);
[button setTitle:#"abc"];}}
};
...
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
dispatch_async(drawingQue, ^{
self.buttonFramesBlock(234,someOtherInt);
});
}
First, never change UI outside of the main thread. So your should modify your code into something like:
dispatch_async(drawingQue, ^{
// ...
dispatch_async(dispatch_get_main_queue(), ^{
button.frame= CGRectMake(x+y, x-y, a, b);
[button setTitle:#"abc"];
});
});
Second, never change UI inside the method shouldAutorotateToInterfaceOrientation. All you have to do inside that method is return whether the view should rotate or not. For instance, in some cases where you have a view controller hierarchy, the view might not get rotated even if you return YES in shouldAutorotateToInterfaceOrientation.
So, you should call your code inside the method:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
This can be achieved in many ways. The simplest (and the one I recommend) is to use a standard Objective-C method:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) { // landscape
[self rotateButtonWithA:234 b:24];
} else { // portrait
[self rotateButtonWithA:123 b:24];
}
}
- (void)rotateButtonWithA:(CGFloat)a b:(CGFloat)b
{
dispatch_async(drawingQue, ^{
// ...
dispatch_async(dispatch_get_main_queue(), ^{
button.frame= CGRectMake(x+y, x-y, a, b);
[button setTitle:#"abc"];
});
});
}
You don't really need to call the block itself from multiple places. But if you still want to do that, there are many answers here that show you how to do that.

Objective C - problem with passing an object name though method parameter when initializing object

I am very new to Objective C and am trying to create a method for initializing an object (button object to be more precise) with only one line of code... My method declaration is...
- (void)buttonDeclaration: (UIButton *)mButton :(int)xloc :(int)yloc :(int)bWidth :(int)bHeight
: (NSString *)sImage :(UIViewController *)mView :(SEL)mSelector
{
mButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self buttonSetxy:mButton :xloc :yloc :bWidth :bHeight];
[mButton setBackgroundImage:[UIImage imageNamed:sImage] forState:UIControlStateNormal];
[mView.view addSubview:mButton];
}
My method call is...
[...buttonDeclaration:newButton :40 :65 :80 :65...]
but when I try to add
[newButton setHidden:FALSE];
after my call to the method it does nothing. I'm not sure what the proper term is, but the object name should be newButton not mButton. Does this make sense and how do I accomplish this?
Actually the way methods are declared in objective c is different.
When you declare method with multiple arguments then it should be like this.
(void) myMethod : (int)firstNum secondArgument:(int)secondNum
so ur method will be declared like
(void)buttonDeclaration: (UIButton *)mButton xPosition:(int)xloc yPosition:(int)yloc Width:(int)bWidth height:(int)bHeight imageName: (NSString *)sImage myView:(UIViewController *)mView selector:(SEL)mSelector
Now you will be calling this method by
[self buttonDeclaration : myBtn xPosition : 5 yPosition : 10 width : 5 height : 10 and so on.......]
and if you want to hide your button, just write
myBtn.hidden = YES;