Apple developer sample of Reachability cause Memory Leak when turning off wifi during play offline music (using this code) in my app and it got stuck line I mentioned bottom, It may cause by that Player code because log says [__NSCFString reachabilityDidChange:]: unrecognized selector
[[NSNotificationCenter defaultCenter] postNotificationName:kReachabilityChangedNotification object:noteObject];
in this method:
static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
{
#pragma unused (target, flags)
NSCAssert(info != NULL, #"info was NULL in ReachabilityCallback");
NSCAssert([(__bridge NSObject*) info isKindOfClass: [CDVReachability class]], #"info was wrong class in ReachabilityCallback");
CDVReachability* noteObject = (__bridge CDVReachability *)info;
// Post a notification to notify the client that the network reachability changed.
[[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotificationString object: noteObject];
}
Here is log:
2015-04-12 19:52:03.416 HelloCordova[4094:1250289] -[__NSCFString reachabilityDidChange:]: unrecognized selector sent to instance 0x174232820
And reachabilityDidChange is in this file
#import "ReachabilityManager.h"
#import "Reachability.h"
#interface ReachabilityManager ()
#property NetworkStatus previousReachability;
#end
#implementation ReachabilityManager
- (id) init {
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reachabilityDidChange:)
name:kReachabilityChangedNotification
object:nil];
self.previousReachability = -1;
}
return self;
}
- (void) reachabilityDidChange:(NSNotification *)notification {
NSLog(#"reachabilityDidChange!");
CDVReachability * r = [notification object];
NetworkStatus ns = [r currentReachabilityStatus];
if (self.delegate && [self.delegate respondsToSelector:#selector(reachabilityDidChangeFrom:to:)]) {
[self.delegate reachabilityDidChangeFrom:self.previousReachability to:ns];
}
self.previousReachability = ns;
}
#end
I don't know is that correct but I try this:
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:kReachabilityChangedNotification object:noteObject];
});}
and even this:
// We're on the main RunLoop, so an NSAutoreleasePool is not necessary, but is added defensively
// in case someon uses the Reachability object in a different thread.
#autoreleasepool {
Reachability* noteObject = (__bridge Reachability*)info;
// Post a notification to notify the client that the network reachability changed.
[[NSNotificationCenter defaultCenter] postNotificationName:kReachabilityChangedNotification object:noteObject];
}
Related
I want to create common class for checking internet connection also it should notify me when internet changes its status.
I am using AFNetworking for checking internet status.
Here is my code i have tried but it is not working please help where i am making mistake??
CheckInternet is my common class derived from NSObject
CheckInternet.h
#import <Foundation/Foundation.h>
#interface CheckInternet : NSObject
+ (void)startNetworkMonitoring;
+ (BOOL)isInternetConnectionAvailable;
#end
CheckInternet.m
#import "CheckInternet.h"
#import "AFNetworking.h"
#implementation CheckInternet
+ (void)startNetworkMonitoring
{
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
NSLog(#"Reachability: %#", AFStringFromNetworkReachabilityStatus(status));
// Check the reachability status and show an alert if the internet connection is not available
switch (status) {
case -1:
// AFNetworkReachabilityStatusUnknown = -1,
NSLog(#"The reachability status is Unknown");
break;
case 0:
// AFNetworkReachabilityStatusNotReachable = 0
NSLog(#"The reachability status is not reachable");
break;
case 1:
NSLog(#"The reachability status is reachable via wan");
[[NSNotificationCenter defaultCenter] postNotificationName:#"reachabilityChanged" object:nil];
break;
case 2:
// AFNetworkReachabilityStatusReachableViaWiFi = 2
NSLog(#"The reachability status is reachable via WiFi");
[[NSNotificationCenter defaultCenter] postNotificationName:#"reachabilityChanged" object:nil];
break;
default:
break;
}
}];
}
#pragma mark - Check Internet Network Status
+ (BOOL)isInternetConnectionAvailable {
return [AFNetworkReachabilityManager sharedManager].reachable;
}
#end
In My ViewController.m (here i want to check internet status)
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[CheckInternet startNetworkMonitoring];
if ([CheckInternet isInternetConnectionAvailable])
{
NSLog(#"---- Internet YES------ ");
}
else
{
NSLog(#"----- Internet NO------ ");
}
}
//This is not called...
- (void)reachabilityChanged:(NSNotification *)notification
{
NSLog(#"---- reachabilityChanged ----");
}
Thank you in advance!
First, I would recommend to start monitoring your network activity in the AppDelegate. Move [CheckInternet startNetworkMonitoring]; to didFinishLaunchingWithOptions
Second, add the following line to case == 0:
[[NSNotificationCenter defaultCenter] postNotificationName:#"reachabilityChanged" object:[NSNumber numberWithInteger:one]];
Also make sure, you post the status of your connection with the notification. Because it is an integer, you need to wrap it in an object (typically into NSNumber).
Third, you need to observe the reachabilityChanged notification.
Add this in your viewDidLoad.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reachabilityChanged:)
name:#"reachabilityChanged"
object:nil];
In your function - (void)reachabilityChanged:(NSNotification *)notification you should be able to access the notification's userInfo property and read the state code there.
Also, do not forget to unobserve the notification once your viewController has disappeared. So in dealloc
[NSNotificationCenter defaultCenter] removeObserver:self];
In Appdelegate.m added postNotification method
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
if(application.applicationState == UIApplicationStateBackground) {
[[NSNotificationCenter defaultCenter] postNotificationName: #"SuggetionPushNotification" object:nil userInfo:userInfo];
AppDelegate *appDel = (AppDelegate *)[[UIApplication sharedApplication] delegate];
SideMenuViewController *leftMenuViewController = [[SideMenuViewController alloc] init];
MFSideMenuContainerViewController *container = [MFSideMenuContainerViewController
containerWithCenterViewController:[[UINavigationController alloc]
initWithRootViewController:[[SuggetionViewController alloc] init]]
leftMenuViewController:leftMenuViewController
rightMenuViewController:Nil];
[self.window makeKeyAndVisible];
appDel.window.rootViewController = container;
}
}
ViewController B (SuggetionViewController) In viewDidLoad
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveTestNotification:)
name:#"SuggetionPushNotification"
object:nil];
- (void) receiveTestNotification:(NSNotification *) notification {
NSLog(#"working");
}
But here not yet fire Notification, if added both post and addobserver in same class then only it fire. what wrong i made. I referred from Send and receive messages through NSNotificationCenter in Objective-C? Please help
Your View Controller B is not in memory when you are posting the notification thats why Controller B is unable to observe the notification. Add Delay before posting the notification will help.
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[[NSNotificationCenter defaultCenter] postNotificationName: #"SuggetionPushNotification" object:nil userInfo:nil];
});
You're calling your notification from the launch method in the app delegate before any view/viewcontroller hierarchy has a change to instantiate and load. The reason you are not getting the notification is because your SuggetionViewController has not been instantiated yet. If you want the notification to be received, you need to fire it after the view controller gets a change to be created.
You can't receive notification inside your SuggetionViewController if it's now loaded.
You add the observer in the viewDidLoad so it can happens that you recieve a remote notification while the SuggetionViewController is not loaded.
You should ensure that your controller is loaded before processing notifications.
In your AppDelegate implementation add this
static void displayStatusChanged(CFNotificationCenterRef center, void *observer,
CFStringRef name, const void *object,
CFDictionaryRef userInfo) {
if (name == CFSTR("com.apple.springboard.lockcomplete")) {
[[NSUserDefaults standardUserDefaults] setBool:YES
forKey:#"kDisplayStatusLocked"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
In applicationDidFinishLaunchingWithOptions, add this below code
CFNotificationCenterAddObserver(
CFNotificationCenterGetDarwinNotifyCenter(), NULL, displayStatusChanged,
CFSTR("com.apple.springboard.lockcomplete"), NULL,
CFNotificationSuspensionBehaviorDeliverImmediately);
Post it in your didRecieveRemoteNotification method.
[[NSNotificationCenter defaultCenter]postNotificationName:#"TestNotification" object:self];
Post this in your view controller viewDidLoad method.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveTestNotification:)
name:#"TestNotification"
object:nil];
Then it will trigger this method.
- (void) receiveTestNotification:(NSNotification *) notification
{
if ([[notification name] isEqualToString:#"TestNotification"]) {
NSLog (#"Successfully received the test notification!");
}
}
Your 'SuggetionViewController' object is not initialized when you post the notification. So there is no 'SuggetionViewController' object to catch the notification.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
if(application.applicationState == UIApplicationStateBackground) {
AppDelegate *appDel = (AppDelegate *)[[UIApplication sharedApplication] delegate];
SideMenuViewController *leftMenuViewController = [[SideMenuViewController alloc] init];
SuggetionViewController *sug_controller = [[SuggetionViewController alloc] init]];
[sug_controller registerNotification];
[[NSNotificationCenter defaultCenter] postNotificationName: #"SuggetionPushNotification" object:nil userInfo:userInfo];
MFSideMenuContainerViewController *container = [MFSideMenuContainerViewController containerWithCenterViewController:[[UINavigationController alloc] initWithRootViewController:sug_controller leftMenuViewController:leftMenuViewController rightMenuViewController:Nil];
[self.window makeKeyAndVisible];
appDel.window.rootViewController = container;
}
}
Add the following instance method to SuggetionViewController
- (void)registerNotification
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receiveTestNotification:) name:#"SuggetionPushNotification" object:nil];
}
- (void)receiveTestNotification
{
NSlog(#"Notification recieved-->>>");
}
I am trying to separate my GUI and my Logic...For example, i have one class which calculates something (calc.h/calc.m). And i have my GUI (viewController.h/viewController.m)...Now i want to update one Element of the GUI from another class... I already tried it with a Singleton, like this:
calc.m:
viewController *con = [viewController sharedInstance];
con.download.text = #"It Works";
viewController.m:
+ (id)sharedInstance {
static id sharedInstance;
#synchronized(self) {
if (!sharedInstance)
sharedInstance = [[viewController alloc] init];
return sharedInstance;
}
}
But it does´t work...
UPDATE:
in my ViewController:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveNotification:)
name:#"UpdateDownloadLabel"
object:nil];
- (void) receiveNotification:(NSNotification *) notification
{
NSDictionary* userInfo = notification.userInfo;
if ([[notification name] isEqualToString:#"Update"])
{
download.text = [userInfo valueForKey:#"Test"];
}
}
And in my other class i post the notification like this:
[[NSNotificationCenter defaultCenter]
postNotificationName:#"UpdateDownloadLabel"
object:nil userInfo:info];
But it doesn't work...
That's not a proper use of a singleton. If you need to notify your UI from your model you have these options:
- delegation
- Local Notification
- KVO
The general rule is to keep your logic decoupled from your presentation, therefore your calc class shouldn't really know that a label named download exists.
Example: How to use notification.
In classA.h file
extern NSString * const ClassADidViewNotification;
In classA.m file
NSString * const ClassADidViewNotification = #"ClassADidViewNotification";
Register for notification in classA.m :-
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserverForName: XXFooDidBarNotification
object:nil
queue:nil
usingBlock:^(NSNotification *notification)
{
NSLog(#"notification.object: %#", notification.object);
}
];
Post Notification:-
ClassB.m
[[NSNotificationCenter defaultCenter] postNotificationName: ClassADidViewNotification object:nil];
I've this scenario, UIViewController => UITableViewController => UITableViewController => UIViewController.
From first UITableViewController, I check internet connection and populate data based on that.
This is what the problem is -
1. At first UITableViewController wi-fi is on so data populated from URL,
2. I turn-off wi-fi and select row and go to second UITableViewController, network status is NotReachable and data is populated from database
3. I turn on wi-fi and select row and go to last UIViewcontroller, here network status is NotReachable whereas it should be ReachableViaWiFi
Am I missing something? Please suggest.
-(void) viewWillAppear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(reachabilityChanged:)
name: kReachabilityChangedNotification
object: nil];
Reachability *reach = [Reachability reachabilityWithHostname: #"www.apple.com"];
[reach startNotifier];
}
- (void) reachabilityChanged: (NSNotification *)notification {
Reachability *reach = [notification object];
if( [reach isKindOfClass: [Reachability class]]) {
NetworkStatus status = [reach currentReachabilityStatus];
switch(status) {
case NotReachable:
{
if (!performedOnce) {
[self processOffline];
performedOnce = YES;
}
[[NSNotificationCenter defaultCenter] removeObserver:self name:kReachabilityChangedNotification object:nil];
}
break;
default:
{
if (!performedOnce) {
[self retrieveUserListWithUrl];
performedOnce = YES;
}
[[NSNotificationCenter defaultCenter] removeObserver:self name:kReachabilityChangedNotification object:nil];
}
break;
}
}
}
I try to send local notification. Here some code for class sending the notification:
#interface Sender : UIView
{
NSInteger itemID;
}
#implementation Sender
-(void) changedProperty
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"NotificationName" object:NULL];
}
And here the code to receive this notification:
#interface Listener : UIViewController
{
}
#implementation Listener
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(selectedItem:) name:#"NotificationName" object:NULL];
}
- (void)viewDidUnload
{
[super viewDidUnload];
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"NotificationName" object:NULL];
}
-(void) selectedItem:(NSNotification *) notification
{
// some actions
}
But this code doesn't work. Debugging I see how postNotificationName: object works but the method selectedItem: doesn't call
UPDATE.
Here is more code. Maybe this will help.
extern const NSString* selectItemNotificationName;
#interface vRoomSelectorItem : UIView
{
RoomSelectorItemBackground backgroundType;
NSInteger itemID;
}
#property NSInteger itemID;
-(void) setBackgroundType:(RoomSelectorItemBackground) backgroundType;
#interface vRoomSelectorItem ()
#property RoomSelectorItemBackground backgroundType;
#end
#implementation vRoomSelectorItem
const NSString* selectItemNotificationName = #"Reservation.SelectRoom";
-(RoomSelectorItemBackground) backgroundType
{
return backgroundType;
}
-(void) setBackgroundType:(RoomSelectorItemBackground)value
{
if(backgroundType != value)
{
backgroundType = value;
[self changedBackgroundType];
}
}
-(void) changedBackgroundType
{
if(backgroundType == RoomSelectorItemFilled)
{
// animation
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:(NSString*)selectItemNotificationName object:NULL userInfo:[[NSDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithInteger:itemID], #"ID", NULL]];
});
}
else
// reverse animation
}
#import "vRoomSelectorItem.h"
#interface vcReservationSelectRoom : UIViewController
{
NSMutableArray* arraySelectorItems;
}
#implementation vcReservationSelectRoom
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(selectedItem:) name:(NSString*)selectItemNotificationName object:NULL];
for(NSInteger i = 1; i <= SELECTOR_ITEM_COUNT; ++i)
{
vRoomSelectorItem* newItem = [[vRoomSelectorItem alloc] initWithFrame:CGRectMake(/*coordinates*/)];
[self.view addSubview:newItem];
[newItem setBackgroundType:RoomSelectorItemTransparent];
[newItem setItemID:i];
[arraySelectorItems addObject:newItem];
newItem = NULL;
}
}
-(void) selectedItem:(NSNotification *) notification
{
// some actions
}
-(void) dealloc
{
arraySelectorItems = NULL;
[[NSNotificationCenter defaultCenter] removeObserver:self name:(NSString*)selectItemNotificationName object:NULL];
}
#end
From your code in the quetion I think you can try this:
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(selectedItem:) name:#"NotificationName" object:NULL];
[self.view changedProperty]; // method of your Sender class
}
It seems like your code should work. Make sure the notification is on the main thread and the workflow is as follows:
Add listener to notification center
Initiate sender
Send notification
You can be sure that it is on the main thread with:
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] post...];
});
Couple things I would change:
remove yourself from NSNotificationCenter in -(void)dealloc instead
of -(void)viewDidUnload. viewDidUnload will be deprecated, and
dealloc could get called without viewDidUnload.
For Notifications, I like to store the name in an external constant
to make sure that I don't typo the string:
//header.h
extern NSString *const notificationName;
//implementation.m
NSString *const notificationName = #"notification";
Ok, I've solved the problem. The piece of code above is not enough to understand, why it has not been working. The object of class vcReservationSelectRoom was temporary and it had been destroyed before sending notification from any vRoomSelectorItem. Sorry for my mistake of not showing enough code to solve this problem. And thank you all for helping.