I have 4 void statements and I want to randomize them so one out of the 4 triggers at a time. Like for example the first void triggers then the next time maybe the third void triggers and so fourth. Could I use arc4random() or do I need another approach?
Sure you can use arc4Random. (It's better to use arc4random_uniform, as #JustSid pointed out in his comments. Off to fix my sample code...) There are a nearly infinite number of ways to do this.
First, a gripe of mine. Don't call methods "voids". That's inaccurate and misleading. (And it makes you sound ignorant about programming.) They're methods. The text inside the parenthesis at the beginning of the method tells you what kind of value it returns. If it doesn't return anything, the word "void" is C language notation for "nothing."
So the method:
-(void) foo;
Takes no parameters and doesn't return anything, where the method:
-(BOOL) bar;
...also takes no parameters, but it returns a boolean result.
The first method is not a "void". It is a method that doesn't return a result.
Now, to your question:
You could do something like this:
- (void) foo;
{
NSLog(#"foo");
}
- (void) bar;
{
NSLog(#"bar");
}
- (void) foobar;
{
NSLog(#"foobar");
}
- (void) randomMethod;
{
int index = arc4random_uniform(3);
switch (index)
{
case 0:
[self foo];
break;
case 1:
[self bar];
break;
case 2:
[self foobar];
break;
}
}
You could also use blocks. You could set up an array of block pointers, use arc4random_uniform() to pick an array index, and execute the appropriate block from the array. (Blocks are objects so you can add them to an array.)
The syntax of blocks and block pointers is a little tricky to follow, so for simplicity I'm not going to write that out. If you're interested I can amend my answer to show how that's done.
arc4random() is perfect for that.
int a = arc4random() % 4;
switch (a) {
case 0:
[self void0];
break;
case 1:
[self void1];
break;
// ...
}
For a scalable solution, you can use NSSelectorFromString.
The NSSelectorFromString will generate this warning: "performSelector may cause a leak because its selector is unknown". If you can't live with the warning, there are solution for that.
NSArray *methods = #[#"method1", #"method2", #"method3", #"method4"]; //add more if needed
int index = arc4random_uniform((int)methods.count);
NSString *selectedMethod = [methods objectAtIndex:index];
SEL s = NSSelectorFromString(selectedMethod);
[self performSelector:s];
-(void)method1
{
NSLog(#"method1 is called");
}
-(void)method2
{
NSLog(#"method2 is called");
}
-(void)method3
{
NSLog(#"method3 is called");
}
-(void)method4
{
NSLog(#"method4 is called");
}
Related
I have a method with a callback that looks something like this:
- (void)doStuff:(void ^())callback
{
//Do a whole bunch of stuff
//Perform callback
callback();
}
I would then call this method later on like this:
[self doStuff:^{[self callbackMethod];}];
This works just fine when there is no data to pass, but now I have some data that I need to pass between the methods.
Take the following method:
- (void)showAViewWithOptions:(int)options
In this method, I show a view with certain options, but if there's something else already on the screen, I call the method to hide it with a callback back to this method.
So the implementation looks like this.
- (void)hideOldView:(void ^())callback
{
//Hide all objects in _oldViews and set _oldViews = nil
callback();
}
- (void)showAViewWithOptions:(int)options
{
if(_oldViews != nil)
{
[self hideOldView:^(int options){[self showAViewWithOptions:options];}];
return;
}
//Show the new view
}
This compiles and runs without issue, but options loses its value after being passed.
Quite frankly, it surprised me that it compiled, since I thought it wouldn't accept a block with arguments.
For instance, if I call [self showAViewWithOptions:4];, when the callback is fired, options = -1730451212.
How do I bind the value options to the block? Or a better question, is this simply not possible because when I call the callback:
callback();
I'm not putting anything into the parentheses?
If so, then a good follow-up question would be: why does this even compile in the first place?
This should work:
- (void)showAViewWithOptions:(int)options
{
if(_oldViews != nil)
{
[self hideOldView:^(){
// Recursion doesn't feel right; be careful!
// Why can't whatever is being done by this call be done
// within this block?
[self showAViewWithOptions:options];
}];
return;
}
//Show the new view
}
A block with a return value and parameters looks like this:
^ return_type (parameter1_type parameter1_name, parameter2_type parameter2_name, ...) {
do_stuff;
};
you can pass vairable into method... Callback method you call inside method:
- (void)hideOldViewWithId:(float)f callback:(void (^)(float f))callback{
f = f + 2.0f;
callback(f);
}
and then call
[self hideOldViewWithId:1.0f callback:^(float f) {
NSLog(#"callback with float: %f", f);
}];
I'm attempting to implement the containsObject but with two parameters, is this possible?
Currently I've got:
if ([ myArray containsObject:#"Object1", #"Object2"]){
return result;
} else {
return NO;
}
and apparently there's too many arguments. I've delved through Apple's docs but I'm yet to find anything. Any suggestions?
Why not just do this?
if ([ myArray containsObject:#"Object1" ] && [ myArray containsObject:#"Object 2" ] ){
return result;
} else {
return NO;
}
There is too many arguments, containsObject is for a single object. (You can read its official documentation here) To fix your problem, use the && operator and call containsObject on each object individually.
if ([myArray containsObject:#"Object1"] && [myArray containsObject#"Object2"]){
return result;
} else {
return NO;
}
You will have to evaluate them individually. Example:
bool MONNSArrayContainsAllObjectsIn(NSArray* const pArray, NSArray* const pSought) {
assert(pArray);
assert(pSought);
assert(0 < pSought.count);
for (id at in pSought) {
if (false == [pArray containsObject:at]) {
return false;
}
}
return true;
}
Then your code above becomes:
return MONNSArrayContainsAllObjectsIn(myArray, #[#"Object1", #"Object2"]);
If you are working with a known number of elements (2 in this case), then you can avoid creating the temporary array -- if you prefer to make that optimization and write out all variants you need, including parameters. Other answers detail this approach.
If you have large arrays and many comparisons to perform, NSSet may be better suited for your task.
typedef void (^RequestProductsCompletionHandler)(BOOL success, NSArray * products);
I am having difficulty understanding what this line of code is doing in .h file.
Please explain in detail
typedef.
void (I know what void do, but whats the purpose here?).
(^RequestProductsCompletionHandler)(BOOL success, NSArray * products);
How to call it?
This is definition of objective-c block type with name RequestProductsCompletionHandler that takes 2 parameters (BOOL and NSArray) and does not have return value. You can call it the same way you would call c function, e.g.:
RequestProductsCompletionHandler handler = ^(BOOL success, NSArray * products){
if (success){
// Process results
}
else{
// Handle error
}
}
...
handler(YES, array);
Vladimir described it well. It defines a variable type which will represent a block that will pass two parameters, a boolean success and an array of products, but the block itself returns void. While you don't need to use the typedef, it makes the method declaration a tad more elegant (and avoids your having to engage in the complicated syntax of block variables).
To give you a practical example, one might infer from the name of the block type and its parameters that this defines a completion block (e.g. a block of code to be performed when some asynchronous operation, like a slow network request, completes). See Using a Block as a Method Argument.
For example, imagine that you had some InventoryManager class from which you could request product information, with a method with an interface defined like so, using your typedef:
- (void)requestProductsWithName:(NSString *)name completion:(RequestProductsCompletionHandler)completion;
And you might use the method like so:
[inventoryManager requestProductsWithName:name completion:^(BOOL success, NSArray * products) {
// when the request completes asynchronously (likely taking a bit of time), this is
// how we want to handle the response when it eventually comes in.
for (Product *product in products) {
NSLog(#"name = %#; qty = %#", product.name, product.quantity);
}
}];
// but this method carries on here while requestProductsWithName runs asynchronously
And, if you looked at the implementation of requestProductsWithName, it could conceivably look something like:
- (void)requestProductsWithName:(NSString *)name completion:(RequestProductsCompletionHandler)completion
{
// initiate some asynchronous request, e.g.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// now do some time consuming network request to get the products here
// when all done, we'll dispatch this back to the caller
dispatch_async(dispatch_get_main_queue(), {
if (products)
completion(YES, products); // success = YES, and return the array of products
else
completion(NO, nil); // success = NO, but no products to pass back
});
});
}
Clearly, this is unlikely to be precisely what your particular completion handler block is doing, but hopefully it illustrates the concept.
Mike Walker created a nice one page site that shows all possibilities to declare a block in Objective-C. This can be helpful to understand your problem as well:
http://fuckingblocksyntax.com
To quote his site, this is how you can define blocks:
As a local variable:
returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...};
As a property:
#property (nonatomic, copy) returnType (^blockName)(parameterTypes);
As a method parameter:
- (void)someMethodThatTakesABlock:(returnType (^)(parameterTypes))blockName {...}
As an argument to a method call:
[someObject someMethodThatTakesABlock: ^returnType (parameters) {...}];
As a typedef:
typedef returnType (^TypeName)(parameterTypes);
TypeName blockName = ^(parameters) {...}
I am calling a function which returns a structure of the type CvBox2D, however I want to check for an error in the function and return NULL if there is an error.
CvBox2D function()
{
...
if(ERROR)
return NULL;
...
}
I am getting an error : cannot convert from 'int' to 'CvBox2D'
Your function return type is CvBox2D, so you can't convert a (NULL) pointer to it.
If you really need to return "nothing" if the check inside the function fails, you can change the return type to a pointer to CvBox2D:
CvBox2D* function()
{
...
}
You will also have to change the way the returned object is created inside your function.
Note that using raw pointers in C++ usually isn't a good idea.
Take a look at std::shared_ptr (available in C++11) if you think you really have to use pointers.
If you want to return some error code, you can do the following:
int function(CvBox2D* output) {
// code...
// Assign to struct.
output->center = ...;
if (error) {
return RC_ERROR_FOO;
}
return RC_OK;
}
Then you call this function using a struct you've already allocated (for example, on the stack):
{
CvBox2D myBox;
int retval = function(&myBox);
if (RC_OK == retval) {
printf("Good! Angle of box: %g", myBox.angle);
} else {
printf("Error: %d", retval);
}
}
Where RC_OK, RC_ERROR_FOO are defined as constant integers, or better, as an enum (if you're using C++).
The other answers solve your problem, but if you want to keep the signature of your function, instead of returning an error code, you should throw an exception.
I using Xcode 4.6 for calculator application and I am getting three errors, can you shed a light what might causing them?
Here are the 3 errors
property 'pushElement' not find on object type "CalculatorBrain",
property 'enter pressed' not found on object type CalculatorviewController
property 'perform operation' not find on object type "CalculatorBrain"
This is part of the code that i am getting error in it which CalculatorviewController.m
- (IBAction)enterPressed:(UIButton*)sender {
[_Brain.pushElement :self.display.text]; ......first error ....
userIntheMiddleOfEnteringText= NO;
}
- (IBAction)operationPressed:(UIButton *)sender {
if (userIntheMiddleOfEnteringText)
self.enterPressed; ........second error.....
else {
double result = [_Brain.performOperation]; ... third error...
self.display.text=[NSString stringWithFormat:#"%g",result];
}
}
and the CalculatorBrain.m code is
#import "CalculatorBrain.h"
#implementation CalculatorBrain
-(void)pushElement:(double)operand {
NSNumber *operandObject=[NSNumber numberWithDouble:operand];
[self.stack addObject:operandObject];
}
-(double) popElement {
NSNumber *popedNumber=[self.stack lastObject];
if (popedNumber)
{
[_stack removeLastObject];
}
return [popedNumber doubleValue];
}
-(double)performOperation:(NSString*)operation {
double result = 0;
if( [operation isEqualToString: #"+"]){
result =self. popElement + self.popElement;
}
else if ([operation isEqualToString: #"*"]){
result=self.popElement*self.popElement;
}
else if ([operation isEqualToString:#"-" ]) {
double operand=self.popElement;
result=self.popElement - operand ;
}
else if ([operation isEqualToString:#"/"]) {
double divisor =self.popElement;
result= self.popElement/ divisor;
}
return result;
}
#end
All three errors are simple errors that indicate that you don't yet understand the basics of Objective-C. You need to learn the language first. Learn how to call methods and pass parameters.
The first error:
[_Brain.pushElement :self.display.text];
This should be:
[_Brain pushElement:self.display.text];
Here you want to call the pushElement: method on the _Brain object.
The second error:
self.enterPressed;
That indicates that you are trying to accessing a property named enterPressed on self. But there is no such property. There is a method named enterPressed: and this takes a UIButton as an argument. So you need to call it like this:
[self enterPress:sender];
The third error:
double result = [_Brain.performOperation];
Here you want to call the performOperation method but there isn't one. There is a method named performOperation:. This takes a parameter of type NSString. It must be called like this:
double result = [_Brain performOperation:#"some operation"];
The missing piece here is the operation you wish to pass. There is no obvious indication of where that comes from. Perhaps the button title.