I have a block function defined as the below:
#property (atomic, assign) bool callInProgress;
//in implementation:
- (void)synchronize:(void(^)(void(^unlock)()))block {
if (!_callInProgress) {
_callInProgress = YES;
[_tableView setScrollEnabled:false];
block(^{
[_tableView setScrollEnabled:true];
_callInProgress = NO;
});
}
}
Then when I do:
[self synchronize:^(void(^unlock)()) {
}];
and I set a break point at that [self synchronize..], the break point gets hit twice no matter what! If I add a body:
/*break point on this line*/ [self synchronize:^(void(^unlock)()) {
NSLog(#"HERE");
unlock();
}];
HERE gets printed ONCE but the break point gets hit twice!
Any ideas why?
The breakpoint is being hit once when you reach the synchronize call, and once when you enter the callback block. Both are on the same line.
Related
Consider I want a integer variable called page and at first time its value will be 1.
See the sample code in sampleViewcontroller.m file,
// sampleViewcontroller.m
- (void)viewDidLoad {
[self method1];
}
-(void) method1
{
// Here I am calling one API
[self reload];
}
-(void)reload
{
// Here I want to use that page value, whenever this method is called, that value should be incremented by one.
// and that page value I am passing as a parameter to another method, see below
MyWebservices *webservices=[MyWebservices sharedInstance];
[webservices getNextPageURL:_nextPageUrl pageValue:page callbackHandler:^(NSError *error,id json,NSString* msg) {
// some code
}
}
in reload method I want use that page value, and want to pass below method as parameter,
[webservices getNextPageURL:_nextPageUrl pageValue:page callbackHandler:^(NSError *error,id json,NSString* msg)
and that method definition written in Webservices.m file, which is called each time when reload method is called. Here I want to take that page value every time, when it is increased and use that page to next api call.
// Webservices.m file
-(void)getNextPageURL:(NSString*)url pageValue:(NSInteger)page callbackHandler:(callbackHandler)block{
_userDefaults=[NSUserDefaults standardUserDefaults];
// I want to print this page value (which is incrementing every time)
NSLog(#"Page value is : %#",page); // incremented value
NSString *urll=[NSString stringWithFormat:#"%#&api_key=%#&ip=%#&token=%#&page=%#",url,API_KEY,IP,[_userDefaults objectForKey:#"token"],page];
[self httpResponseGET:urll parameter:#"" callbackHandler:^(NSError *error,id json,NSString* msg) {
dispatch_async(dispatch_get_main_queue(), ^{
block(error,json,msg);
});
}];
}
so I want to pass page value from reload method to below method, so it will take incremented value at each time.
-(void)getNextPageURL:(NSString*)url pageValue:(NSInteger)page callbackHandler:(callbackHandler)block
write following in your .h file:
#property (nonatomic) NSInteger page;
now in .m file
- (void)viewDidLoad {
self.page = 1; // as you initially want page value to be 1.
[self method1];
}
-(void) method1
{
// Here I am calling one API
self.page = page + 1; // as you want to increment the value of page when before function is called
[self reload];
}
-(void)reload
{
// Here I want to use that page value, whenever this method is called, that value should be incremented by one.
// and that page value I am passing as a parameter to another method, see below
MyWebservices *webservices=[MyWebservices sharedInstance];
[webservices getNextPageURL:_nextPageUrl pageValue:self.page callbackHandler:^(NSError *error,id json,NSString* msg) {
// some code
}
}
Just hold a private property in your class, e.g.
#interface SampleViewcontroller()
#property (nonatomic) NSInteger page;
#end
#implementation
- (void)viewDidLoad {
self.page = 0;
[self method1];
}
-(void) method1
{
// Here I am calling one API
[self reload];
}
-(void)reload
{
// Here I want to use that page value, whenever this method is called, that value should be incremented by one.
// and that page value I am passing as a parameter to another method, see below
MyWebservices *webservices=[MyWebservices sharedInstance];
[webservices getNextPageURL:_nextPageUrl pageValue:self.page callbackHandler:^(NSError *error,id json,NSString* msg) {
// some code
if (!error) {
//If success
self.page++;
}
}
}
#end
You don't need to add a property, just add a private variable using anyone from the following ways:
1. In .h file:
#interface ViewController : UIViewController {
NSInteger page;
}
OR
2. In .m file:
#interface ViewController ()
#end
#implementation ViewController
NSInteger page;
// Now use the page variable in .m file:
- (void)viewDidLoad {
[self method1];
}
-(void) method1
{
// Here I am calling one API
[self reload];
}
-(void)reload
{
// Here I want to use that page value, whenever this method is called, that value should be incremented by one.
// and that page value I am passing as a parameter to another method, see below
MyWebservices *webservices=[MyWebservices sharedInstance];
[webservices getNextPageURL:_nextPageUrl pageValue:page callbackHandler:^(NSError *error,id json,NSString* msg) {
// some code
page ++;
}
}
I've written a category on UIView that allows me to walk the view hierarchy:
UIView+Capture.h
typedef void(^MSViewInspectionBlock)(UIView *view, BOOL *stop);
#interface UIView (Capture)
- (void)inspectViewHeirarchy:(MSViewInspectionBlock)block;
#end
UIView+Capture.m
#implementation UIView (Capture)
- (void)inspectViewHeirarchy:(MSViewInspectionBlock)block
{
BOOL stop = NO;
[self inspectViewHeirarchy:block stop:stop];
}
#pragma - Private
- (void)inspectViewHeirarchy:(MSViewInspectionBlock)block stop:(BOOL)stop
{
if (!block || stop) {
return;
}
block(self, &stop);
for (UIView *view in self.subviews) {
[view inspectViewHeirarchy:block stop:stop];
if (stop) {
break;
}
}
}
#end
Which you can use like so:
[[[UIApplication sharedApplication] keyWindow] inspectViewHeirarchy:^(UIView *view, BOOL *stop) {
if ([view isMemberOfClass:[UIScrollView class]]) {
NSLog(#"Found scroll view!");
*stop = YES;
}
}];
Everything works fine, except setting stop to YES. This appears to have absolutely no effect whatsoever. Ideally, I'd like this to halt the recursion, so when I've found the view I want to take some action on I don't have to continue to traverse the rest of the view hierarchy.
I'm pretty dense when it comes to using blocks, so it may be something completely obvious. Any help will be greatly appreciated.
The way you're using a block is exactly the same as using a C function. So there's nothing special you really need to know about blocks. Your code should work but note the difference between passing stop as a BOOL * to your block and to create a new local when you recurse.
It looks like you're expecting calls down to inspectViewHierarchy:stop: to affect the outer stop variable. That won't happen unless you pass it as a reference. So I think what you want is:
- (void)inspectViewHeirarchy:(MSViewInspectionBlock)block stop:(BOOL *)stop
...and appropriate other changes.
I assume you want to return all the way out from the top-level inspectViewHierarchy when the user sets stop to YES.
(Incidentally, you spelled “hierarchy” wrong and you should use a prefix on methods you add to standard classes.)
#implementation UIView (Capture)
- (void)micpringle_visitSubviewsRecursivelyWithBlock:(MSViewInspectionBlock)block
{
BOOL stop = NO;
[self inspectViewHierarchy:block stop:&stop];
}
#pragma - Private
- (void)micpringle_visitSubviewsRecursivelyWithBlock:(MSViewInspectionBlock)block stop:(BOOL *)stop
{
block(self, stop);
if (*stop)
return;
for (UIView *view in self.subviews) {
[view micpringle_visitSubviewsRecursivelyWithBlock:block stop:stop];
if (*stop)
break;
}
}
#end
- (BOOL) inspectViewHeirarchy:(MSViewInspectionBlock)block
{
BOOL stop = NO;
block(self, &stop);
if (stop)
return YES;
for (UIView *view in self.subviews) {
if ([view inspectViewHeirarchy:block])
return YES;
}
return NO;
}
Try this:
- (void)inspectViewHeirarchy:(MSViewInspectionBlock)block
{
__block BOOL stop = NO;
[self inspectViewHeirarchy:block stop:stop];
}
Blocks, by nature, copy the variables and context in which they are declared.
Even though you are passing the boolean as a reference, it's possible that it's using a copy of the context and not the true stop.
This is just a wild guess but, inside inspectViewHierarchy:stop: do something like:
- (void)inspectViewHeirarchy:(MSViewInspectionBlock)block stop:(BOOL)stop
{
if (!block || stop) {
return;
}
// Add these changes
__block BOOL blockStop = stop;
block(self, &blockStop);
for (UIView *view in self.subviews) {
[view inspectViewHeirarchy:block stop:stop];
if (stop) {
break;
}
}
}
This may be a long shot and I'm not 100% sure it will work without having your project, but it's worth a shot.
Also, refactor your method so "heirarchy" is actually spelled "hierarchy" :] It's good for reusability and for keeping a good code base ;)
wouldn't you want to check the status of 'stop' directly after you invoke the block? It doesn't help to invoke it after you call inspectViewHierarchy:stop: because you are passing a copy of 'stop' to that method instead of the reference.
i have two views MainView and MainInvoicing
from MainView i am sending a int type variable value to MainInvoicing
this is my code
in MainInvoicing.h file i declared int type var
#property (nonatomic, assign) int myChoice;
in MainView.m file on a button click i am calling the MainInvoicing as
MainInvoicing *invoicing = [[MainInvoicing alloc] initWithNibName:#"Invoicing" bundle:nil];
[self presentViewController:invoicing animated:YES completion:nil];
invoicing.myChoice = 1;
from this my MainInvoicing is called perfectly, but myChoice is equal to zero '0'. while it should be '1'
i am receiving this value in MainInvoicing.m as
- (void)viewDidLoad
{
[super viewDidLoad];
[self Start];
}
and the start method is
- (void) Start
{
switch (myChoice)
{
case 1:
NSLog(#"value is %d",myChoice);
break;
case 2:
NSLog(#"value is %d",myChoice);
break;
default:
NSLog(#"Oooopppss...%d",myChoice);
break;
}
}
i am always on default part ….
where am i wrong or any suggestion to get the right value … please help…
You should assign value before you present view controller:
MainInvoicing *invoicing = [[MainInvoicing alloc] initWithNibName:#"Invoicing" bundle:nil];
invoicing.myChoice = 1;
[self presentViewController:invoicing animated:YES completion:nil];
As Greg said in his comment, it's all about order of code. If you look at your code you should see the order in which it is called.
MainInvoicing *invoicing = [[MainInvoicing alloc] initWithNibName:#"Invoicing" bundle:nil];
[self presentViewController:invoicing animated:YES completion:nil];
[self Start];
switch (myChoice)
{
case 1:
NSLog(#"value is %d",myChoice);
break;
case 2:
NSLog(#"value is %d",myChoice);
break;
default:
NSLog(#"Oooopppss...%d",myChoice);
break;
}
invoicing.myChoice = 1;
So you are trying to access the myChoice variable before you set it, which is why you are getting 0 instead of 1.
With all of that said, there is a possibility of your code working as it is written since the viewDidLoad function is not always called serially. But you should not expect it to always work and you should initialize variables before you call code that could use them immediately.
Accept Greg's answer, I was just adding a bit of explanation to the problem.
One of my methods (mySecondMethod) receives a block and need to add an additional treatment to this block before passing it as an argument to another method.
Here is the code sample:
- (void)myFirstMethod {
// some code
__weak MyController *weakSelf = self;
[self mySecondMethod:^(BOOL finished) {
[weakSelf doSomething:weakSelf.model.example];
}];
}
- (void)mySecondMethod:(void(^)(BOOL finished))completion {
void (^modifiedCompletion)(BOOL) = ^void(BOOL finished){
completion(finished);
_messageView.hidden = YES; //my new line
};
[UIView animateWithDuration:duration animations:^{
//my code
} completion:modifiedCompletion];
}
When run, I got a bad access error on completion(finished) line. completion is NULL. I tried to copy the block like this:
void (^copiedCompletion)(BOOL) = [completion copy];
void (^modifiedCompletion)(BOOL) = ^void(BOOL finished){
copiedCompletion(finished);
_messageView.hidden = YES; // my new line
};
but still got the error.
When I empty out completion block, the crash still happen so the crash is not due to what is inside.
Any idea how to solve this? Thanks!
I think you are getting the bad access because of this
// some code
__weak MyController *weakSelf = self;
[self mySecondMethod:^(BOOL finished) {
[weakSelf doSomtehing:weakSelf.model.example];
}];
Try changing it to.
id example = self.model.example;
[self mySecondMethod:^(BOOL finished) {
[self doSomething:example];
}];
Edit
The block needs to be copied before being called.
Side Note
Do a check before calling blocks to avoid unexpected crashes.
if (completion) {
completion(finished);
}
I have a method that draws a one sprite on the screen with the animation effects,
if I call in init this method
if( (self=[super init]) ) {
// ....
[self myMethod];
// .....
}
Then he does it once on my project
When I call by schedule
-(void)schedulMyMethod:(ccTime)dt {
[self myMethod];
}
if( (self=[super init]) ) {
// ....
[self schedule:#selector(schedulMyMethod:) interval:0.5];
// .....
}
It runs for an unlimited times
I need so that I can call the my method some amount
You mean, you want to have it repeat N times? You'll need to keep state on times remaining for it to run.
#property (nonatomic, assign) NSInteger timesToRunMyMethod;
- (void)beginRunningMyMethod {
self.timesToRunMyMethod = 100; // N==100
[self myMethod];
}
- (void)myMethod {
self.timesToRunMyMethod--;
// do stuff
if (self.timesToRunMyMethod > 0) {
// i used native delayed execution, you can replace it with whatever cocos2d offers if you want
[self performSelector:#selector(myMethod) withObject:nil afterDelay:0.5];
}
}
And it's probably wrong to start this on init. Is it a view controller? Then you can use viewDidAppear or willAppear.