I test internet connection with Reachability and dispatch_async(dispatch_get_main_queue()
when I test the following code it works but it is called multiple times.
Parent:
#protocol RootViewDelegate <NSObject>
#optional
-(void)internetIsDownGoToRoot;
#end
- (void)testInternetConnection
{
internetReachableFoo = [ReachabilityTony reachabilityWithHostname:#"www.google.com"];
__weak typeof(self) weakSelf = self;
// Internet is reachable
internetReachableFoo.reachableBlock = ^(ReachabilityTony *reach)
{
// Update the UI on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Yayyy, we have the interwebs!");
[weakSelf sendLoginRequest];
});
};
// Internet is not reachable
internetReachableFoo.unreachableBlock = ^(ReachabilityTony *reach)
{
// Update the UI on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Someone broke the internet :(");
CloudConnection *sharedInstance=[CloudConnection sharedInstance];
sharedInstance.isUserLoggedIN=NO;
//update login button
[weakSelf updateButtons];
[weakSelf notifyChild];
});
};
[internetReachableFoo startNotifier];
}
-(void)viewDidAppear:(BOOL)animated
{
[self testInternetConnection];
}
-(void)viewWillDisappear:(BOOL)animated
{
internetReachableFoo= nil;
}
//notify childs no connection come back to root
-(void) notifyChild
{
[delegate internetIsDownGoToRoot];
}
Child:
-(void)viewDidAppear:(BOOL)animated
{
NSArray *viewControllers = self.navigationController.viewControllers;
int count = [viewControllers count];
id previousController = [viewControllers objectAtIndex:count - 2];
RootViewController *rvc= previousController;
rvc.delegate=self;
}
-(void)internetIsDownGoToRoot
{
//internet connection is no avaliable go to root
[self.navigationController popToRootViewControllerAnimated:YES];
}
So this is parentview lets say I push-pop childview 5 times and shutdown internet. I see on nslog that
Someone broke the internet :(
Someone broke the internet :(
Someone broke the internet :(
Someone broke the internet :(
Someone broke the internet :(
as you can see I have added internetReachableFoo= nil; but I doesnt change anything.
Whats going on with above code, why it is called multiple times?
What is the possible dangers of using this block?
It's called multiple times because every time you pop the child, the root gets -viewDidAppear: and calls -testInternetConnection, which re-runs the reachability test.
Update: Ok you've changed your question slightly. The reason why you're getting 5 "did disappear" messages is because you never stop the notifier. Reachability keeps itself alive as long as it's running, so nilling out your reference isn't killing it. You need to explicitly say [internetReachableFoo stopNotifier] before nilling it out.
Related
While loading when we click on back button or any cell of the table the action is called multiple times after loading is finish.Here the the code snippet that what i'm doing when I start the loading and stop the loading.
+(void)showLoader_OnView{
APP_DELEGATE.window.userInteractionEnabled = NO;
[MBProgressHUD showHUDAddedTo:APP_DELEGATE.window animated:YES];
}
To stop the loading:-
+(void)hideLoader {
APP_DELEGATE.window.userInteractionEnabled =YES;
[MBProgressHUD hideAllHUDsForView:APP_DELEGATE.window animated:YES];
}
please help me.
Update
actually i'm taking data from server. whenever user will go to next window then in viewWillAppear function i call a function which will hit the api to get the data.
-(void)performAutoSync
{
#try
{
if(self.shouldPerformAutoSync)//Necessary conditions to check the auto sync
{
[AppConstants showLoader_OnView]; //here i call the loader.
self.shouldPerformAutoSync = NO;
if(!self.isSyncing)
{
if(!syncBl)
{
syncBl = [[SyncBL alloc] init];
syncBl.delegate = self;
}
if(!syncDl)
syncDl = [[SyncDL alloc] init];
// [self saveModifiedDataForCurrentViewController];
[self delayToAutoSync];
NSMutableDictionary *dictMainData = [NSMutableDictionary new];
[dictMainData setObject:[syncDl fetchCompleteDataAndPrepareDictionary:YES] forKey:#"data"];//#"MainData"];
[syncBl performAutoSync:dictMainData];
}
}
}
#catch (NSException *exception) {
BILog(#"%#",exception);
}
}
Don't block the main thread.
Seeing that you invoke [AppConstants showLoader_OnView] from performAutoSync, and that showLoader_OnView in turn executes:
[MBProgressHUD showHUDAddedTo:APP_DELEGATE.window animated:YES]
I can only assume that performAutoSync is executed in the main thread. This, of course, blocks the UI until your operations are completed.
You should redesign so that you won't need all your state variables, globals, global calls, and take advantage of multi-threading.
Also, remove this, as it qualifies as a kludge;
APP_DELEGATE.window.userInteractionEnabled = NO
What is the correct way to fire methods within a completion block (if this is even recommended)? Right now, I have an IBAction that calls a method that downloads information with a completion block signifying if the info was retrieved successfully or not. If it was, I want to push a view controller that will display that information, but at the moment, nothing is happening. I'm guessing it has something to do with main thread, gcd, etc...
__weak YTTMSetupViewController *weakSelf = self;
[mc downloadJson:^(BOOL success) {
if(success){
NSLog(#"sucess. metric count - %i",(int)mc.collection.count);
//info was downloaded. Push new view controller with info
YTTMMetricTableViewController *mtvc = [self.storyboard instantiateViewControllerWithIdentifier:#"YTTMMetricTableViewController"];
mtvc.group = (WAGroup*)[[WAMetricCollection sharedInstance].collection lastObject];
mtvc.hidesBottomBarWhenPushed = YES;
[weakSelf.navigationController pushViewController:mtvc animated:YES];
}
else{
NSLog(#"failure");
//display failure UI
}
NSLog(#"end of downloading");
[HUD dismissAfterDelay:0.5f animated:YES];
}];
Not sure if this is the right way to do it, but it worked.
I added a method that will push the vc on the main thread as so:
[weakSelf performSelectorOnMainThread:#selector(pushDetail) withObject:nil waitUntilDone:YES];
Completed Code:
__weak YTTMSetupViewController *weakSelf = self;
[mc downloadJson:^(BOOL success) {
if(success){
NSLog(#"sucess. metric count - %i",(int)mc.collection.count);
//info was downloaded. Push new view controller with info
[weakSelf performSelectorOnMainThread:#selector(pushDetail) withObject:nil waitUntilDone:YES];
}
else{
NSLog(#"failure");
//display failure UI
}
NSLog(#"end of downloading");
}];
}
-(void)pushDetail{
__weak YTTMSetupViewController *weakSelf = self;
YTTMMetricTableViewController *mtvc = [self.storyboard instantiateViewControllerWithIdentifier:#"YTTMMetricTableViewController"];
mtvc.group = (WAGroup*)[[WAMetricCollection sharedInstance].collection lastObject];
mtvc.hidesBottomBarWhenPushed = YES;
[weakSelf.navigationController pushViewController:mtvc animated:YES];
}
You could simply try wrapping the call with a dispatch_asynch block...
__weak YTTMSetupViewController *weakSelf = self;
[mc downloadJson:^(BOOL success) {
if(success){
NSLog(#"sucess. metric count - %i",(int)mc.collection.count);
dispatch_async(dispatch_get_main_queue(), ^{
//info was downloaded. Push new view controller with info
YTTMMetricTableViewController *mtvc = [self.storyboard instantiateViewControllerWithIdentifier:#"YTTMMetricTableViewController"];
mtvc.group = (WAGroup*)[[WAMetricCollection sharedInstance].collection lastObject];
mtvc.hidesBottomBarWhenPushed = YES;
[weakSelf.navigationController pushViewController:mtvc animated:YES];
});
}
else{
NSLog(#"failure");
//display failure UI
}
NSLog(#"end of downloading");
[HUD dismissAfterDelay:0.5f animated:YES];
}];
All UI updates must be performed on the main thread. Personally I prefer to do this through GCD as it produces more readable code than performSelectorOnMainThread. However, there's nothing wrong with performSelectorOnMainThread aside from personal preference in the case of calling a single UI update on the main thread following the execution of some completion block. Do note that, whichever one you choose, you should be consistent with what you use to guarantee that blocks are enqueued in the order you specified.
Working code aside, however, the convention Apple's frameworks seem to use is to perform all completion blocks on the main thread unless a queue is specified as a method parameter, in which case the completion block should be performed on that queue. So in this case I would recommend you edit your download handler class's downloadJson method to automatically perform the completion block on the main queue.
I have a simple app which loads text from a RSS when there is no internet it displays a empty tableView. I would like to make it so that when there is no internet it gives some text saying there is no internet available and a button to try to reconnect.
In the attempt to make this I used Tony Million's Reachability class as in this question.
I set a boolean to YES and NO in the functions like this :
- (void)testInternetConnection
{
internetReachableFoo = [Reachability reachabilityWithHostname:#"www.google.com"];
// Internet is reachable
internetReachableFoo.reachableBlock = ^(Reachability*reach)
{
// Update the UI on the main thread
dispatch_async(dispatch_get_main_queue(), ^
{
NSLog(#"Yayyy, we have the interwebs!");
internetConnect = YES;
});
};
// Internet is not reachable
internetReachableFoo.unreachableBlock = ^(Reachability*reach)
{
// Update the UI on the main thread
dispatch_async(dispatch_get_main_queue(), ^
{
NSLog(#"Someone broke the internet :(");
internetConnect = NO;
});
};
[internetReachableFoo startNotifier];
}
Now when I try to check the boolean in the viewDidLoad function it will always return before the function is finished. This is because the Reachability class is in a background thread. I don't know how I can let my code wait for the result before proceeding.
So I should let my code wait for result and then depending on the result make the tableView disappear and change it to text with a button.
I want to know :
How to make my code wait for result of the background thread.
How to reconnect. (with a loading bar or something to let the user
know it is searching for connection).
You could also check the reachability as
NSInteger reachabilityStatus = 0;
reachabilityStatus = [self checkNetworkReachability];
if (reachabilityStatus) {
//network is available so perform network oriented task;
} else {
// show an alert saying network is unavailable;
}
- (NSInteger)checkNetworkReachability {
networkReachability = [Reachability reachabilityForInternetConnection];
NetworkStatus networkStatus = [networkReachability currentReachabilityStatus];
if (networkStatus == NotReachable) {
NSLog(#"There IS NO internet connection");
} else {
NSLog(#"There IS internet connection");
}
return (NSInteger)networkStatus;
}
Err... ...why don't put the code with you want to go on with into the block? Do you really need that BOOL?
You may wrap your network requests into a reachability block as well anyway, return with an error when there is no connection, wire up that specific error to the UI.
A bit pseudo, but you'd get the idea. The point is that you need connection only when you want to request something, no need for monitoring every time I think. Using eppz!reachability you can get reachability status on demand, with blocks those gets called once, not every time when reachability changes.
Also you're probably interested in the host you're trying to reach, not google.com. Your feed can be unreachable while google.com works fine.
-(void)fetchFeed
{
[EPPZReachability reachHost:#"your.rss.host.com"
completion:^(EPPZReachability *reachability)
{
if (reachability.reachable)
{
[self hideRetryUI]; // UI
[self fetchFeed:^(Feed *feed) // Networking
{ [self showFeed:feed]; }]; // UI (probably table view reload)
}
else
{
[self showRetryUI]; // UI
}
}];
}
-(IBAction)retryTouchedUp
{ [self fetchFeed]; }
I'm trying to make a chat. I've googled for samples and I followed several of them without success in this post I've asked for the first troubles I've found and I've applied every answer without success.
Which I'm trying is in an UIViewController I load to 2 subclasses of UIView with one UITableView each one. In one of the views I'll load the users list and in other the messages sended with a selected user from the first class.
First I've tried to use threading within each of the classes with the content but was crashing because memory warnings.
Now I'm using a NSTimer in the UIViewController that calls to update both classes, but still crashing.
This is the code:
-(void)chatStartUpdating
{
chatIsUpdating = YES;
chatLoop = [NSTimer scheduledTimerWithTimeInterval:5.0f target:self selector:#selector(backgroundChatUpdate) userInfo:nil repeats:YES];
}
-(void)chatStopUpdating
{
if (chatLoop == nil) return;
[chatLoop invalidate];
chatLoop = nil;
chatIsUpdating = NO;
}
-(void)updateChat
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[chatBar updateList:nil];
[chat messagesMustBeUpdated:nil];
[pool release];
}
-(void)backgroundChatUpdate
{
[self performSelectorOnMainThread:#selector(updateChat) withObject:nil waitUntilDone:NO];
//[self performSelectorInBackground:#selector(updateChat) withObject:nil];
}
If I run in background the list of messages become slow in scrolling, and after a few updates I start receiving mem warnings and the app crashes.
the methods Start and Stop updating are called from Main Thread when user pushes the chat button or some occurrence of the users list.
Anyone knows a good example of code to do something similar? Or point me to the right way?
Thanks in advance.
EDIT----
Those are the classes inside the 2 UIViews that retrieves data from remote API, parse the results in a class to contain it and populates the tableview with results:
-(void)updateList:(id)sender
{
isReading = YES;
users = [OTChatDataManager readUsers];
[list reloadData];
NSLog(#"ChatBar Was updated");
isReading = NO;
}
-(void)messagesMustBeUpdated:(id)sender
{
isReading = YES;
iSQLResult *newMessages = [OTChatDataManager readMessagesFromUser:fromUserId toUser:toUserId sinceLastMessageId:lastMessageId];
[self mergeMessages:newMessages];
[list reloadData];
[newMessages release];
isReading = NO;
}
All the properties of the 2 lists are declared as atomic, and I tried each solution proposed in the link of this post, GDC, Locks, etc...
Consider this setup:
Object A creates object B for doing some work, and sets itself as B's delegate to be informed of work progress.
B does some work with GCD blocks, and signals back to A with the delegate method about work completion. A wants to tear down (release) B upon work completion.
In code terms:
Object A:
B *b = [[B alloc] init];
b.delegate = self;
[b doSomeWork];
- (void) didSomeWorkFromB:(B *)b {
[b release];
b = nil;
}
Object B:
- (void) doSomeWork {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
doSomeWork();
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Work is complete.");
[self.delegate didSomeWorkFromB:self];
});
});
}
PROBLEM: calling [b release] inside object A causes a crash. I think it's because the dispatch queue/background code is still running when A tries to release B.
QUESTION: how do I properly set up the objects and signaling in this case, to make sure that A only destroys B when all the background work has been completed?
Bogus question. It actually works as expected and the code above does not crash. The crash was caused by some unrelated code.
You are correct, that the code works as is. But it is unnecessarily complicated.
You can just have B's doSomeWork retain itself (either by explicitly calling [self retain] and [self release] in doSomeWork or just by referencing self in the dispatch_async block, which will retain it for us), and let A clean up immediately after the invocation of doSomeWork, and therefore no further cleanup is required in didSomeWorkFromB.
This pattern is very common in iOS. For example, if you look at many common implementations of NSURLConnection, since it retains itself while the connection is downloading, and releases itself once the connection is done, we often don't both keeping a reference to the connection and cleaning it up in connectionDidFinishLoading. Just let the magic of reference counting memory management take care of everything for you.
In A:
- (void) test
{
B *b = [[B alloc] init];
b.delegate = self;
[b doSomeWork];
[b release]; // you could also autorelease above, but I just wanted to make it more explicit for the purposes of the demonstration
}
- (void) didSomeWorkFromB:(B *)b
{
// [b release]; // don't need to release it ... we already did
// b = nil; // certainly don't need to nil local reference ... this does nothing useful in any scenario
}
In B:
- (void) dealloc
{
// let's log this so we can see when it's deallocated
NSLog(#"%s", __FUNCTION__);
[super dealloc];
}
- (void) doSomeWork
{
// [self retain]; // you could manually retain if you want
NSLog(#"%s", __FUNCTION__);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(10);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Work is complete.");
[self.delegate didSomeWorkFromB:self];
// [self release]; // and if you manually retained, you'd manually release, too
});
});
}
In my opinion, this approach (of having A release B immediately after calling doSomeWork) is more robust, more closely coordinating the balancing cleanup of the object. I also think this puts you in better stead as you contemplate an eventual shift to ARC.