In application Showing Reachability error in ios6.Can any one please help me to sort out this error in reachability ios6,PLease do the needfully.
#implementation Reachability
static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
{
#pragma unused (target, flags)
NSCAssert(info != NULL, #"info was NULL in ReachabilityCallback");
NSCAssert([(NSObject*) info isKindOfClass: [Reachability class]], #"info was wrong class in ReachabilityCall9back");
//We're on the main RunLoop, so an NSAutoreleasePool is not necessary, but is added defensively
// in case someon uses the Reachablity object in a different thread.
Reachability* noteObject = (Reachability*) info;
// Post a notification to notify the client that the network reachability changed.
[[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject];
}
- (BOOL) startNotifier
{
BOOL retVal = NO;
SCNetworkReachabilityContext context = {0, self, NULL, NULL, NULL};
if(SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context))
{
if(SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode))
{
retVal = YES;
}
}
return retVal;
}
Error:
Cast of C pointer type 'void *' to Objective-C pointer type 'NSObject *' requires a bridged cast
id objInfo = (__bridge id)info;
NSCAssert([objInfo isKindOfClass: [Reachability class]], #"info was wrong class in ReachabilityCall9back");
Reachability* noteObject = (Reachability*) objInfo ;
Related
Here is my reference code. I have put break point in startnotifier method but it is not being called.
[[NSNotificationCenter defaultCenter] addObserver: self selector: #selector(reachabilityChanged:) name: kReachabilityChangedNotification object: nil];
internetReach = [[Reachability reachabilityForInternetConnection] retain];
[internetReach startNotifier];
I have written this part of code in appdelegate.m (didFinishLaunchingWithOptions).
I have declared var in appdelegate.h like below....
#interface AppDelegate : UIResponder < UIApplicationDelegate >
{
Reachability *internetReach;
Reachability *wifiReach;
Reachability *hostReach;
}
Why breakpoint in startnotifier is not being called and hence nsnotification is not calling observer function if I change the network.
+(BOOL)ConnectedToNetWork
{
Reachability *HostReach = [Reachability reachabilityForInternetConnection];
NetworkStatus internetStatus = [HostReach currentReachabilityStatus];
bool result = false;
if (internetStatus == ReachableViaWiFi)
result = true;
else if(internetStatus==ReachableViaWWAN)
result = true;
return result;
}
use this method whre ever u want to check connection
I'm embedded in an environment (Adobe AIR) where I cannot override didFinishLaunchingWithOptions. Is there any other way to get those options? Are they stored in some global variable somewhere? Or does anyone know how to get those options in AIR?
I need this for Apple Push Notification Service (APNS).
Following the path in the link Michiel left ( http://www.tinytimgames.com/2011/09/01/unity-plugins-and-uiapplicationdidfinishlaunchingnotifcation/ ), you can create a class who's init method adds an observer to the UIApplicationDidFinishLaunchingNotification key. When the observer method is executed, the launchOptions will be contained in the notification's userInfo. I was doing this with local notifications so this was the implementation of my class:
static BOOL _launchedWithNotification = NO;
static UILocalNotification *_localNotification = nil;
#implementation NotificationChecker
+ (void)load
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(createNotificationChecker:)
name:#"UIApplicationDidFinishLaunchingNotification" object:nil];
}
+ (void)createNotificationChecker:(NSNotification *)notification
{
NSDictionary *launchOptions = [notification userInfo] ;
// This code will be called immediately after application:didFinishLaunchingWithOptions:.
UILocalNotification *localNotification = [launchOptions objectForKey: #"UIApplicationLaunchOptionsLocalNotificationKey"];
if (localNotification)
{
_launchedWithNotification = YES;
_localNotification = localNotification;
}
else
{
_launchedWithNotification = NO;
}
}
+(BOOL) applicationWasLaunchedWithNotification
{
return _launchedWithNotification;
}
+(UILocalNotification*) getLocalNotification
{
return _localNotification;
}
#end
Then when my extension context is initialized I check the NotificationChecker class to see if the application was launched with a notification.
BOOL appLaunchedWithNotification = [NotificationChecker applicationWasLaunchedWithNotification];
if(appLaunchedWithNotification)
{
[UIApplication sharedApplication].applicationIconBadgeNumber = 0;
UILocalNotification *notification = [NotificationChecker getLocalNotification];
NSString *type = [notification.userInfo objectForKey:#"type"];
FREDispatchStatusEventAsync(context, (uint8_t*)[#"notificationSelected" UTF8String], (uint8_t*)[type UTF8String]);
}
Hope that helps someone!
getting problem is that and how can solve this .I trying the internet is On than my must go for SYNC process .but i was work how can do this.
[TDatasyncmanager chekNetworkStatus:]':
:incompatible types in initialization
:switch quantity not an integer
I am trying to check the internet connection and host network connection like this
TDatasyncmanager.h file
#import "Reachability.h"
#interface TDatasyncmanager : UIViewController
{
Reachability *internetReachable;
Reachability *hostReachable;
BOOL internetActive;
NSMutableData *webData;
}
-(void)chekNetworkStatus:(NSNotification*)notice;
#end
And in TDatasyncmanager.m file
#import "TDatasyncmanager.h"
#import "Reachability.h"
#implementation TDatasyncmanager
#synthesize internetActive;
- (void)viewDidLoad {
[super viewDidLoad];
//check for internet connection
[[NSNotification defaultCenter] addObserver:self selector:#selector(checkNetworkStatus:) name:kReachabilityChangedNotification object:nil];
[internetReachable startNotifier];
//check for apathwa to arandom host exits
hostReachable = [[Reachability reachabilityWithHostName:#"http://www.google.com"]retain];
[hostReachable startNotifier];
if(internetActive == YES)
{
NSLog(#"tokenapi:%# annan %#",tokenapi,Contentid);
NSString *post;
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSLog(#"%#",postLength);
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:#"http://192.168.0.1:88/"]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (theConnection) {
webData = [[NSMutableData data] retain];
NSLog(#"%#",webData);
//[theConnection start];
}
}
}
-(void)chekNetworkStatus:(NSNotification *)notice
{
**//called after network status changes
NetworkStatus *internetStatus = [internetReachable currentReachabilityStatus];//:incompatible types in initialization
switch(internetStatus)//:switch quantity not an integer**
{
case NotReachable:
{
NSLog(#"The internet is down.");
self.internetActive = NO;
break;
}
case ReachableViaWiFi:
{
NSLog(#"The internet is working via WIFI.");
self.internetActive = YES;
break;
}
case ReachableViaWWAN:
{
NSLog(#"The internet is working via WWAN.");
self.internetActive = YES;
break;
}
default:
{
NSLog(#"The internet by defualt is Not working.");
self.internetActive = NO;
break;
}
}
NetworkStatus hostStatus = [hostReachable currentReachabilityStatus];
switch (hostStatus)
{
case NotReachable:
{
NSLog(#"The internet is down.");
self.internetActive = NO;
break;
}
case ReachableViaWiFi:
{
NSLog(#"The internet is working Via WIFI.");
self.internetActive = YES;
break;
}
case ReachableViaWWAN:
{
NSLog(#"The internet is working Via WWAN.");
self.internetActive = YES;
break;
}
default:
{
NSLog(#"the internet is down.");
self.internetActive= NO;
}
}
}
for Checking Network Present Status We Need Two Classes
Reachability.h
#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>
typedef enum {
NotReachable = 0,
ReachableViaWiFi,
ReachableViaWWAN
} NetworkStatus;
#define kReachabilityChangedNotification #"kNetworkReachabilityChangedNotification"
#interface Reachability: NSObject
{
BOOL localWiFiRef;
SCNetworkReachabilityRef reachabilityRef;
}
//reachabilityWithHostName- Use to check the reachability of a particular host name.
+ (Reachability*) reachabilityWithHostName: (NSString*) hostName;
//reachabilityWithAddress- Use to check the reachability of a particular IP address.
+ (Reachability*) reachabilityWithAddress: (const struct sockaddr_in*) hostAddress;
//reachabilityForInternetConnection- checks whether the default route is available.
// Should be used by applications that do not connect to a particular host
+ (Reachability*) reachabilityForInternetConnection;
//reachabilityForLocalWiFi- checks whether a local wifi connection is available.
+ (Reachability*) reachabilityForLocalWiFi;
//Start listening for reachability notifications on the current run loop
- (BOOL) startNotifer;
- (void) stopNotifer;
- (NetworkStatus) currentReachabilityStatus;
//WWAN may be available, but not active until a connection has been established.
//WiFi may require a connection for VPN on Demand.
- (BOOL) connectionRequired;
#end
Reachability.m
#import <sys/socket.h>
#import <netinet/in.h>
#import <netinet6/in6.h>
#import <arpa/inet.h>
#import <ifaddrs.h>
#import <netdb.h>
#import <CoreFoundation/CoreFoundation.h>
#import "Reachability.h"
#define kShouldPrintReachabilityFlags 1
static void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char* comment)
{
#if kShouldPrintReachabilityFlags
NSLog(#"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n",
(flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-',
(flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-',
(flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-',
(flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-',
(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-',
(flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
(flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-',
(flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-',
(flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-',
comment
);
#endif
}
#implementation Reachability
static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
{
#pragma unused (target, flags)
NSCAssert(info != NULL, #"info was NULL in ReachabilityCallback");
NSCAssert([(NSObject*) info isKindOfClass: [Reachability class]], #"info was wrong class in ReachabilityCallback");
//We're on the main RunLoop, so an NSAutoreleasePool is not necessary, but is added defensively
// in case someon uses the Reachablity object in a different thread.
NSAutoreleasePool* myPool = [[NSAutoreleasePool alloc] init];
Reachability* noteObject = (Reachability*) info;
// Post a notification to notify the client that the network reachability changed.
[[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject];
[myPool release];
}
- (BOOL) startNotifer
{
BOOL retVal = NO;
SCNetworkReachabilityContext context = {0, self, NULL, NULL, NULL};
if(SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context))
{
if(SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode))
{
retVal = YES;
}
}
return retVal;
}
- (void) stopNotifer
{
if(reachabilityRef!= NULL)
{
SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
}
}
- (void) dealloc
{
[self stopNotifer];
if(reachabilityRef!= NULL)
{
CFRelease(reachabilityRef);
}
[super dealloc];
}
+ (Reachability*) reachabilityWithHostName: (NSString*) hostName;
{
Reachability* retVal = NULL;
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);
if(reachability!= NULL)
{
retVal= [[[self alloc] init] autorelease];
if(retVal!= NULL)
{
retVal->reachabilityRef = reachability;
retVal->localWiFiRef = NO;
}
}
return retVal;
}
+ (Reachability*) reachabilityWithAddress: (const struct sockaddr_in*) hostAddress;
{
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress);
Reachability* retVal = NULL;
if(reachability!= NULL)
{
retVal= [[[self alloc] init] autorelease];
if(retVal!= NULL)
{
retVal->reachabilityRef = reachability;
retVal->localWiFiRef = NO;
}
}
return retVal;
}
+ (Reachability*) reachabilityForInternetConnection;
{
struct sockaddr_in zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sin_len = sizeof(zeroAddress);
zeroAddress.sin_family = AF_INET;
return [self reachabilityWithAddress: &zeroAddress];
}
+ (Reachability*) reachabilityForLocalWiFi;
{
[super init];
struct sockaddr_in localWifiAddress;
bzero(&localWifiAddress, sizeof(localWifiAddress));
localWifiAddress.sin_len = sizeof(localWifiAddress);
localWifiAddress.sin_family = AF_INET;
// IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0
localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);
Reachability* retVal = [self reachabilityWithAddress: &localWifiAddress];
if(retVal!= NULL)
{
retVal->localWiFiRef = YES;
}
return retVal;
}
#pragma mark Network Flag Handling
- (NetworkStatus) localWiFiStatusForFlags: (SCNetworkReachabilityFlags) flags
{
PrintReachabilityFlags(flags, "localWiFiStatusForFlags");
BOOL retVal = NotReachable;
if((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect))
{
retVal = ReachableViaWiFi;
}
return retVal;
}
- (NetworkStatus) networkStatusForFlags: (SCNetworkReachabilityFlags) flags
{
PrintReachabilityFlags(flags, "networkStatusForFlags");
if ((flags & kSCNetworkReachabilityFlagsReachable) == 0)
{
// if target host is not reachable
return NotReachable;
}
BOOL retVal = NotReachable;
if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0)
{
// if target host is reachable and no connection is required
// then we'll assume (for now) that your on Wi-Fi
retVal = ReachableViaWiFi;
}
if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) ||
(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0))
{
// ... and the connection is on-demand (or on-traffic) if the
// calling application is using the CFSocketStream or higher APIs
if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0)
{
// ... and no [user] intervention is needed
retVal = ReachableViaWiFi;
}
}
if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN)
{
// ... but WWAN connections are OK if the calling application
// is using the CFNetwork (CFSocketStream?) APIs.
retVal = ReachableViaWWAN;
}
return retVal;
}
- (BOOL) connectionRequired;
{
NSAssert(reachabilityRef != NULL, #"connectionRequired called with NULL reachabilityRef");
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
{
return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
}
return NO;
}
- (NetworkStatus) currentReachabilityStatus
{
NSAssert(reachabilityRef != NULL, #"currentNetworkStatus called with NULL reachabilityRef");
NetworkStatus retVal = NotReachable;
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
{
if(localWiFiRef)
{
retVal = [self localWiFiStatusForFlags: flags];
}
else
{
retVal = [self networkStatusForFlags: flags];
}
}
return retVal;
}
#end
After That In Which Class You Want To Know STATUS ....
.H
#class Reachability
Reachability* internetReachable;
Reachability* hostReachable;
.m
internetReachable =[Reachability reachabilityForInternetConnection];
NetworkStatus internetStatus = [internetReachable currentReachabilityStatus];
switch (internetStatus)
{
case Not Reachable:
{
// No Internet Connection
}
default:
{ // Internet Available
}
}
NOTE - it's NSNotificationCenter you want to be referencing - not just NSNotification.
During a test, a client noticed that video playback in the iPhone pauses when headphones are unplugged. He wanted similar functionality for audio playback, and maybe the ability to pop up a message.
Does anyone know if there's an event of some kind I could hook into to make this possible?
See Responding to Route Changes from the Audio Session Programming Guide.
This changed with iOS 7, you just need to listen to the notification named AVAudioSessionRouteChangeNotification
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(audioRouteChanged:) name:AVAudioSessionRouteChangeNotification object:nil];
Swift 3.0 #snakeoil's solution:
NotificationCenter.default.addObserver(self, selector: #selector(YourViewController.yourMethodThatShouldBeCalledOnChange), name: NSNotification.Name.AVAudioSessionRouteChange, object: nil)
Here's the full implementation I eventually used for sending events when the headphones are plugged in (and unplugged).
There was a fair amount of complexity I needed to deal with to ensure things still worked after the app was returned from the background.
CVAudioSession.h file
#import <Foundation/Foundation.h>
#define kCVAudioInputChangedNotification #"kCVAudioInputChangedNotification"
#define kCVAudioInterruptionEnded #"kCVAudioInterruptionEnded"
#interface CVAudioSession : NSObject
+(void) setup;
+(void) destroy;
+(NSString*) currentAudioRoute;
+(BOOL) interrupted;
#end
CVAudioSession.m file
#import "CVAudioSession.h"
#import <AudioToolbox/AudioToolbox.h>
#implementation CVAudioSession
static BOOL _isInterrupted = NO;
+(void) setup {
NSLog(#"CVAudioSession setup");
// Set up the audio session for recording
OSStatus error = AudioSessionInitialize(NULL, NULL, interruptionListener, (__bridge void*)self);
if (error) NSLog(#"ERROR INITIALIZING AUDIO SESSION! %ld\n", error);
if (!error) {
UInt32 category = kAudioSessionCategory_RecordAudio; // NOTE CANT PLAY BACK WITH THIS
error = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(category), &category);
if (error) NSLog(#"couldn't set audio category!");
error = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, propListener, (__bridge void*) self);
if (error) NSLog(#"ERROR ADDING AUDIO SESSION PROP LISTENER! %ld\n", error);
UInt32 inputAvailable = 0;
UInt32 size = sizeof(inputAvailable);
// we do not want to allow recording if input is not available
error = AudioSessionGetProperty(kAudioSessionProperty_AudioInputAvailable, &size, &inputAvailable);
if (error) NSLog(#"ERROR GETTING INPUT AVAILABILITY! %ld\n", error);
// we also need to listen to see if input availability changes
error = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioInputAvailable, propListener, (__bridge void*) self);
if (error) NSLog(#"ERROR ADDING AUDIO SESSION PROP LISTENER! %ld\n", error);
error = AudioSessionSetActive(true);
if (error) NSLog(#"CVAudioSession: AudioSessionSetActive (true) failed");
}
}
+ (NSString*) currentAudioRoute {
UInt32 routeSize = sizeof (CFStringRef);
CFStringRef route;
AudioSessionGetProperty (kAudioSessionProperty_AudioRoute,
&routeSize,
&route);
NSString* routeStr = (__bridge NSString*)route;
return routeStr;
}
+(void) destroy {
NSLog(#"CVAudioSession destroy");
// Very important - remove the listeners, or we'll crash when audio routes etc change when we're no longer on screen
OSStatus stat = AudioSessionRemovePropertyListenerWithUserData(kAudioSessionProperty_AudioRouteChange, propListener, (__bridge void*)self);
NSLog(#".. AudioSessionRemovePropertyListener kAudioSessionProperty_AudioRouteChange returned %ld", stat);
stat = AudioSessionRemovePropertyListenerWithUserData(kAudioSessionProperty_AudioInputAvailable, propListener, (__bridge void*)self);
NSLog(#".. AudioSessionRemovePropertyListener kAudioSessionProperty_AudioInputAvailable returned %ld", stat);
AudioSessionSetActive(false); // disable audio session.
NSLog(#"AudioSession is now inactive");
}
+(BOOL) interrupted {
return _isInterrupted;
}
// Called when audio is interrupted for whatever reason. NOTE: doesn't always call the END one..
void interruptionListener( void * inClientData,
UInt32 inInterruptionState) {
if (inInterruptionState == kAudioSessionBeginInterruption)
{
_isInterrupted = YES;
NSLog(#"CVAudioSession: interruptionListener kAudioSessionBeginInterruption. Disable audio session..");
// Try just deactivating the audiosession..
OSStatus rc = AudioSessionSetActive(false);
if (rc) {
NSLog(#"CVAudioSession: interruptionListener kAudioSessionBeginInterruption - AudioSessionSetActive(false) returned %.ld", rc);
} else {
NSLog(#"CVAudioSession: interruptionListener kAudioSessionBeginInterruption - AudioSessionSetActive(false) ok.");
}
} else if (inInterruptionState == kAudioSessionEndInterruption) {
_isInterrupted = NO;
// Reactivate the audiosession
OSStatus rc = AudioSessionSetActive(true);
if (rc) {
NSLog(#"CVAudioSession: interruptionListener kAudioSessionEndInterruption - AudioSessionSetActive(true) returned %.ld", rc);
} else {
NSLog(#"CVAudioSession: interruptionListener kAudioSessionEndInterruption - AudioSessionSetActive(true) ok.");
}
[[NSNotificationCenter defaultCenter] postNotificationName:kCVAudioInterruptionEnded object:(__bridge NSObject*)inClientData userInfo:nil];
}
}
// This is called when microphone or other audio devices are plugged in and out. Is on the main thread
void propListener( void * inClientData,
AudioSessionPropertyID inID,
UInt32 inDataSize,
const void * inData)
{
if (inID == kAudioSessionProperty_AudioRouteChange)
{
CFDictionaryRef routeDictionary = (CFDictionaryRef)inData;
CFNumberRef reason = (CFNumberRef)CFDictionaryGetValue(routeDictionary, CFSTR(kAudioSession_AudioRouteChangeKey_Reason));
SInt32 reasonVal;
CFNumberGetValue(reason, kCFNumberSInt32Type, &reasonVal);
if (reasonVal != kAudioSessionRouteChangeReason_CategoryChange)
{
NSLog(#"CVAudioSession: input changed");
[[NSNotificationCenter defaultCenter] postNotificationName:kCVAudioInputChangedNotification object:(__bridge NSObject*)inClientData userInfo:nil];
}
}
else if (inID == kAudioSessionProperty_AudioInputAvailable)
{
if (inDataSize == sizeof(UInt32)) {
UInt32 isAvailable = *(UInt32*)inData;
if (isAvailable == 0) {
NSLog(#"AUDIO RECORDING IS NOT AVAILABLE");
}
}
}
}
#end
In the 3.1 SDk, Apple added support for associated objects.
However, the simulator will not compile code that includes references to objc_setAssociatedObject, objc_getAssociatedObject, et al. (Undeclared errors)
Is there away around this? Can I make the iPhone simulator compile this code? I would hate to have to do all testing on the device.
Update
Bug Filed:
rdar://7477326
I don't think this will be fixed in the 3.x SDKs, so another fix is to just define the functions and call through to the next definition via a dynamic lookup.
Header:
#if TARGET_IPHONE_SIMULATOR
enum {
OBJC_ASSOCIATION_ASSIGN = 0,
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
OBJC_ASSOCIATION_RETAIN = 01401,
OBJC_ASSOCIATION_COPY = 01403
};
typedef uintptr_t objc_AssociationPolicy;
void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy);
id objc_getAssociatedObject(id object, void *key);
void objc_removeAssociatedObjects(id object);
#endif
Implementation:
#if TARGET_IPHONE_SIMULATOR
void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy) {
((void (*)(id, void *, id, objc_AssociationPolicy))
dlsym(RTLD_NEXT, "objc_setAssociatedObject")) (object, key, value, policy);
}
id objc_getAssociatedObject(id object, void *key) {
return ((id (*)(id, void *))
dlsym(RTLD_NEXT, "objc_getAssociatedObject"))(object, key);
}
void objc_removeAssociatedObjects(id object) {
((void (*)(id))
dlsym(RTLD_NEXT, "objc_removeAssociatedObjects"))(object);
}
#endif
A quick and dirty workaround (largely untested, may be buggy):
#if TARGET_IPHONE_SIMULATOR
#import <objc/runtime.h>
enum {
OBJC_ASSOCIATION_ASSIGN = 0,
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
OBJC_ASSOCIATION_RETAIN = 01401,
OBJC_ASSOCIATION_COPY = 01403
};
typedef uintptr_t objc_AssociationPolicy;
#implementation NSObject (OTAssociatedObjectsSimulator)
static CFMutableDictionaryRef theDictionaries = nil;
static void Swizzle(Class c, SEL orig, SEL new) // swizzling by Mike Ash
{
Method origMethod = class_getInstanceMethod(c, orig);
Method newMethod = class_getInstanceMethod(c, new);
if (class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
else
method_exchangeImplementations(origMethod, newMethod);
}
- (NSMutableDictionary *)otAssociatedObjectsDictionary
{
if (!theDictionaries)
{
theDictionaries = CFDictionaryCreateMutable(NULL, 0, NULL, &kCFTypeDictionaryValueCallBacks);
Swizzle([NSObject class], #selector(dealloc), #selector(otAssociatedObjectSimulatorDealloc));
}
NSMutableDictionary *dictionary = (id)CFDictionaryGetValue(theDictionaries, self);
if (!dictionary)
{
dictionary = [NSMutableDictionary dictionary];
CFDictionaryAddValue(theDictionaries, self, dictionary);
}
return dictionary;
}
- (void)otAssociatedObjectSimulatorDealloc
{
CFDictionaryRemoveValue(theDictionaries, self);
[self otAssociatedObjectSimulatorDealloc];
}
#end
void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy)
{
NSCAssert(policy == OBJC_ASSOCIATION_RETAIN_NONATOMIC, #"Only OBJC_ASSOCIATION_RETAIN_NONATOMIC supported");
[[object otAssociatedObjectsDictionary] setObject:value forKey:[NSValue valueWithPointer:key]];
}
id objc_getAssociatedObject(id object, void *key)
{
return [[object otAssociatedObjectsDictionary] objectForKey:[NSValue valueWithPointer:key]];
}
void objc_removeAssociatedObjects(id object)
{
[[object otAssociatedObjectsDictionary] removeAllObjects];
}
#endif