objc_setAssociatedObject unavailable in iPhone simulator - iphone

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

Related

Block sms in Iphone iOS

While using app I want to block all incoming calls and SMS. It should work at least on jailbroken iphone. I have this code that block calls, but not SMS - how can I fix that?
importCoreTelephony/CoreTelephonyDefines.h>
extern NSString const *kCTSMSMessageReceivedNotification; extern
NSString const *kCTSMSMessageReplaceReceivedNotification; extern
NSString const *kCTSIMSupportSIMStatusNotInserted; extern NSString
const *kCTSIMSupportSIMStatusReady;
typedef struct __CTCall CTCall; extern NSString
CTCallCopyAddress(void, CTCall *);
void * CTSMSMessageSend(id server,id msg); typedef struct
__CTSMSMessage CTSMSMessage; NSString *CTSMSMessageCopyAddress(void *, CTSMSMessage *); NSString *CTSMSMessageCopyText(void *, CTSMSMessage
*);
int CTSMSMessageGetRecordIdentifier(void * msg); NSString *
CTSIMSupportGetSIMStatus(); NSString *
CTSIMSupportCopyMobileSubscriberIdentity();
id CTSMSMessageCreate(void* unknow/always 0/,NSString*
number,NSString* text); void * CTSMSMessageCreateReply(void*
unknow/always 0/,void * forwardTo,NSString *text);
id CTTelephonyCenterGetDefault(void); void
CTTelephonyCenterAddObserver(id, id, CFNotificationCallback, NSString
*, void ,int); void CTTelephonyCenterRemoveObserver(id,id,NSString,void*); int
CTSMSMessageGetUnreadCount(void);
void * CTCallDisconnect(CTCall *call);
static void callback(CFNotificationCenterRef center, void *observer,
CFStringRef name, const void *object, CFDictionaryRef userInfo) {
NSString *notifyname = (__bridge NSString *)name;
if ([notifyname
isEqualToString:#"kCTCallIdentificationChangeNotification"]) {
NSDictionary *info = (__bridge NSDictionary *)userInfo;
CTCall *call = (__bridge CTCall *)[info objectForKey:#"kCTCall"];
NSString *caller = CTCallCopyAddress(NULL, call);
NSLog(#"RECEIVED CALL: %#", caller);
CTCallDisconnect(call);
} }
static void signalHandler(int sigraised) {
printf("\nInterrupted.\n"); exit(0); }
In viewdidload:
id ct = CTTelephonyCenterGetDefault();
CTTelephonyCenterAddObserver(ct, NULL, callback, NULL, NULL,
CFNotificationSuspensionBehaviorHold);
sig_t oldHandler = signal(SIGINT, signalHandler); if (oldHandler ==
SIG_ERR) {
printf("Could not establish new signal handler"); exit(1); }
printf("Starting run loop and watching for notification.\n");
CFRunLoopRun();
i think you have made some syntatical error in following line ,check it
id CTSMSMessageCreate(void* unknow/*always 0*/,NSString* number,NSString* text);
void * CTSMSMessageCreateReply(void* unknow/*always 0*/,void * forwardTo,NSString *text);

Reachability prob ios6

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 ;

Block calls and sms in Iphone iOS while application running

while using app I want to block all incoming calls and sms. How can I do this? I know example link http://tech.ruimaninfo.com/?p=83. But there are errors in code.It should work at least on jailbroken iphone.
Below is some code that I've adapted from that website, and works for me on my non jailbroken device:
Firstly add "CoreTelephony.framework" to your project then import it into your view controller like so:
#import <CoreTelephony/CoreTelephonyDefines.h>
Next, add the following code:
extern NSString const *kCTSMSMessageReceivedNotification;
extern NSString const *kCTSMSMessageReplaceReceivedNotification;
extern NSString const *kCTSIMSupportSIMStatusNotInserted;
extern NSString const *kCTSIMSupportSIMStatusReady;
typedef struct __CTCall CTCall;
extern NSString *CTCallCopyAddress(void*, CTCall *);
void * CTSMSMessageSend(id server,id msg);
typedef struct __CTSMSMessage CTSMSMessage;
NSString *CTSMSMessageCopyAddress(void *, CTSMSMessage *);
NSString *CTSMSMessageCopyText(void *, CTSMSMessage *);
int CTSMSMessageGetRecordIdentifier(void * msg);
NSString * CTSIMSupportGetSIMStatus();
NSString * CTSIMSupportCopyMobileSubscriberIdentity();
id CTSMSMessageCreate(void* unknow/*always 0*/,NSString* number,NSString* text);
void * CTSMSMessageCreateReply(void* unknow/*always 0*/,void * forwardTo,NSString *text);
id CTTelephonyCenterGetDefault(void);
void CTTelephonyCenterAddObserver(id, id, CFNotificationCallback, NSString *, void *,int);
void CTTelephonyCenterRemoveObserver(id,id,NSString*,void*);
int CTSMSMessageGetUnreadCount(void);
void * CTCallDisconnect(CTCall *call);
static void callback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
NSString *notifyname = (__bridge NSString *)name;
if ([notifyname isEqualToString:#"kCTCallIdentificationChangeNotification"]) {
NSDictionary *info = (__bridge NSDictionary *)userInfo;
CTCall *call = (__bridge CTCall *)[info objectForKey:#"kCTCall"];
NSString *caller = CTCallCopyAddress(NULL, call);
NSLog(#"RECEIVED CALL: %#", caller);
CTCallDisconnect(call);
}
}
static void signalHandler(int sigraised) {
printf("\nInterrupted.\n");
exit(0);
}
And in viewDidLoad or some other appropriate method, add the following:
id ct = CTTelephonyCenterGetDefault();
CTTelephonyCenterAddObserver(ct, NULL, callback, NULL, NULL, CFNotificationSuspensionBehaviorHold);
sig_t oldHandler = signal(SIGINT, signalHandler);
if (oldHandler == SIG_ERR) {
printf("Could not establish new signal handler");
exit(1);
}
printf("Starting run loop and watching for notification.\n");
CFRunLoopRun();
For now this code is merely a proof of concept and would have to be expanded upon for error handling and whatnot, but you should get the idea.

class variables in objective-c and memory management

#implementation ProductController
NSString *areaName = nil;
+ (void)setAreaName:(NSString *)areaName_ {
areaName = areaName_;
}
#end
and
#implementation ProductController
NSString *areaName = nil;
+ (void)setAreaName:(NSString *)areaName_ {
if(areaName_ != areaName) {
[areaName release];
areaName = [areaName_ copy];
}
}
- (void)dealloc {
[areaName release];
}
#end
Now which one is correct?and why?
As you seem to understand, there are no "class variables" in Obj-C. The workaround is just a C-style (global, or file-scoped) variable that you set up similarly to how you've shown above. First off, you should use file scope for these variables by marking them with the static keyword:
static NSString *areaName = nil;
You might also consider using a convention like FirstLetterUppercase to indicate the scope difference.
As for memory management, you can treat it exactly like an instance variable, but one that never goes away forever:
static NSString *AreaName = nil;
+ (void)setAreaName:(NSString *)name {
if (![name isEqualToString:AreaName]) {
[AreaName release];
AreaName = [name copy];
}
}
Note that in your second example, you should NOT release the "class" variable from an instance's -dealloc method. If you have more than one instance of the object, this leaves a bad dangling pointer, and defeats the purpose of the "class" variable anyways. Generally, when you use this pattern, you'll "leak" (for some definition of leak) the class variable value, and that's OK.
class variables are generally bad style.
nevertheless, an alternative to other answers would be to create a static dictionary for your lib/app's class variables. a very primitive implementation would take this form:
// MONLibraryClassVariable.h
extern id MONLibraryClassVariableGetObjectForKey(NSString * key);
extern void MONLibraryClassVariableSetObjectForKey(id<NSObject> object, NSString * key);
// MONLibraryClassVariable.m
/* #todo make all this thread safe */
static NSMutableDictionary * MONLibraryClassVariables_ = nil;
id MONLibraryClassVariableGetObjectForKey(NSString * key) {
return [MONLibraryClassVariables_ objectForKey:key];
}
void MONLibraryClassVariableSetObjectForKey(id<NSObject> object, NSString * key) {
if (nil == MONLibraryClassVariables_) {
MONLibraryClassVariables_ = [NSMutableDictionary new];
}
[MONLibraryClassVariables_ setObject:object forKey:key];
}
// ProductController.m
static NSString * const ProductController_KEY_areaName = #"ProductController.areaName";
#implementation ProductController
+ (void)setAreaName:(NSString *)inAreaName {
MONLibraryClassVariableSetObjectForKey([[inAreaName copy] autorelease], ProductController_KEY_areaName);
}
- (void)dealloc {
// nope [areaName release];
[super dealloc];
}
#end

ALAssets groups enumeration: how to check for completion?

ALAssets use a separated thread to manage enumeration, i have to know when enumeration terminate.
The block prototype for group enumeration is :
typedef void (^ALAssetsLibraryGroupsEnumerationResultsBlock)(ALAssetsGroup *group, BOOL *stop);
How can i add a completion block ?
I found a solution that is documented only in part.
When group enumeration is terminated, ALAssetsLibraryGroupsEnumerationResultsBlock is invoked with group=nil. So you can write something like:
void (^groupsEnumerator)(ALAssetsGroup *,BOOL *) = ^(ALAssetsGroup *group, BOOL *stop){
if (group != nil) {
[group enumerateAssetsUsingBlock:assetsEnumerator];
}else {
NSLog(#"group enumeration terminated");
}
};
The same solution is valid for assets enumeration (this is not documented -.- )
void (^assetsEnumerator)(ALAsset *,NSUInteger,BOOL*) = ^(ALAsset *result, NSUInteger index, BOOL *stop){
if (result !=nil) {
//do something with result asset
}else {
NSLog(#"Assets enumeration terminated");
}
};
I'm using this:
[group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if (result == nil) {
return;
}
if (index + 1 == group.numberOfAssets) {
//Do what you want. Im using delegate to notify my parent class about finish.
[delegate didGroupEnumerated:group];
}
}];