iOS - [Class classMethod] results in unrecognized selector sent - ios

Due to code privacy, I have to omit part of info. Just excuse me.
In TokenRequestManager.m, there is an instance method serialQueue4TokenType:
- (dispatch_queue_t)serialQueue4TokenType:(TokenType)tokenType
{
static NSMutableDictionary *serialQueueDict = nil;
if (serialQueueDict == nil) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
serialQueueDict = [[NSMutableDictionary alloc] initWithCapacity:TokenTypeCount];
});
}
dispatch_queue_t queue;
#synchronized (self) {
NSString *key = [TokenManager tokenName4Type:tokenType];
key = [key stringByAppendingString:NSStringFromClass([self class])];
NSValue *value = serialQueueDict[key];
if (value == nil) {
queue = dispatch_queue_create(key.UTF8String, DISPATCH_QUEUE_SERIAL);
value = [NSValue valueWithBytes:&queue objCType:#encode(dispatch_queue_t)];
serialQueueDict[key] = value;
} else {
[value getValue:&queue];
}
}
return queue;
}
Just below the #synchronized (self) { line, it calls the Class method:
+ (NSString *)tokenName4Type:(TokenType)tokenType
{
NSString *string = nil;
switch (tokenType) {
case TokenTypeBiz:
return #"TokenTypeBiz";
break;
case TokenTypeWeb:
default:
string = #"TokenTypeWeb";
break;
}
return string;
}
The code just stands there, but sometimes (a few times) the app will crash at this line with unrecognized selector sent, no matter debug or release.
Any tips will be much appreciated. Thanks.

Related

Why does dispatch_get_specific not recognize the queue I created?

I created a serial queue and set the ID to the queue with dispatch_queue_set_specific, but calling this queue in other classes, I cannot use the dispatch_get_specific test to enter the if statement.
here is my create code:
+ (dispatch_queue_t)sharedFollowerQueue
{
return [[self shareInstance] contextQueue];
}
- (dispatch_queue_t)contextQueue
{
static dispatch_queue_t sharedFollowerQueue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedFollowerQueue = dispatch_queue_create("com.weuseSoftware.Instracker.UserManageTool.followerQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_set_specific(sharedFollowerQueue, sharedFollowerQueueKey, (__bridge void *)self, NULL);
});
return sharedFollowerQueue;
}
here is my calling code,but I can't enter the if statement
- (void)queryForADayFollowersCountWithCallBack:(void (^)(NSArray <GraphEntity *> *))callBack
{
dispatch_queue_t queue = [[self class] sharedFollowerQueue];
NSMutableArray *graphArray = [NSMutableArray array];
dispatch_async(queue, ^{
if (dispatch_get_specific(sharedFollowerQueueKey)) {
NSLog(#"----get in----");
RLMArray *currentDayUsers = [self currentDayUsers];
if (currentDayUsers.count == 0) {
return;
}
for (int i = 0; i < currentDayUsers.count; i++) {
MainUser *user = currentDayUsers[i];
GraphEntity *gEntity = [[GraphEntity alloc] init];
gEntity.userDate = user.createDate;
gEntity.number = user.user.followed_by_count.integerValue;
[graphArray addObject:gEntity];
}
dispatch_async(dispatch_get_main_queue(), ^{
if (callBack) {
callBack([graphArray copy]);
}
});
}
});
}
I create my key
static const void* const sharedFollowerQueueKey = &sharedFollowerQueueKey;
when I log key in create code, I got the key adress
(lldb) po sharedFollowerQueueKey
0x00000001032c41d0
then in my calling code,I got the key adress
(lldb) po sharedFollowerQueueKey
0x00000001032c33e8
why they are different?

Realm+JSON: EXC BAD ACCESS when calling createOrUpdateInRealm:withJSONArray: second time

+ (NSDictionary *)mc_inboundMapping {
static NSMutableDictionary *mappingForClassName = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
mappingForClassName = [NSMutableDictionary dictionary];
});
#synchronized(mappingForClassName) {
NSDictionary *mapping = mappingForClassName[[self className]];//<----EXC BAD ACCESS here
if (!mapping) {
SEL selector = NSSelectorFromString(#"JSONInboundMappingDictionary");
if ([self respondsToSelector:selector]) {
mapping = MCValueFromInvocation(self, selector);
}
else {
mapping = [self mc_defaultInboundMapping];
}
mappingForClassName[[self className]] = mapping;
}
return mapping;
}
I'm using Realm+JSON with Realm. And got a problem with Realm+JSON.
When I attempt to call createOrUpdateInRealm:withJSONArray: multiple time in loop for multiple RLMObjects, the first RLMObject works OK, but the second RLMObject(no matter what class is) fails with EXC BAD ACCESS code1on static variable.
I think autorelease pool deallocate pointing NSMutableDictionary.Anybody got this issue? I'm using XCode 6.3.2 and Realm 0.96.3.
+ (NSArray *)createOrUpdateInRealm:(RLMRealm *)realm withJSONArray:(NSArray *)array {
NSInteger count = array.count;
NSMutableArray *result = [NSMutableArray array];
for (NSInteger index=0; index*kCreateBatchSize<count; index++) {
NSInteger size = MIN(kCreateBatchSize, count-index*kCreateBatchSize);
#autoreleasepool
{
for (NSInteger subIndex=0; subIndex<size; subIndex++) {
NSDictionary *dictionary = array[index*kCreateBatchSize+subIndex];
id object = [self createOrUpdateInRealm:realm withJSONDictionary:dictionary];
[result addObject:object];
}
}
}
return [result copy];
}
calling here...
[realm beginWriteTransaction];
if ([collection isKindOfClass:[NSDictionary class]])
{
for (NSString *key in [collection allKeys])
{
NSLog(#"Set Data == %#:%#", key, collection[key]);
id jsData = [collection[key] objectForKey:#"data"];
if ([key isEqualToString:STATIC_ERROR_MSGS])
{
[SErrorMsgs createOrUpdateInRealm:realm withJSONArray:jsData];
}
else if ([key isEqualToString:STATIC_EPISODE])
{
[SEpisode createOrUpdateInRealm:realm withJSONArray:jsData];
}
else if ([key isEqualToString:STATIC_INGAME_TUTORIAL])
{
[SIngameTutorial createOrUpdateInRealm:realm withJSONArray:jsData];
} //more cases.........edited

#sychronized alternative in class method

I want to have a method that will either create a new object or return an existing one based on an identifier string.
This is what I have:
#implementation MyObject {
}
#synthesize data = _data;
- (instancetype)init
{
self = [super init];
if (self) {
}
return self;
}
// these methods are the only ones to be used for managing the MyObject life cycle
+ (NSMutableDictionary *)objectsDict
{
static NSMutableDictionary *map = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
map = [NSMutableDictionary new];
});
return map;
}
+ (MyObject *)getRefrenceForId:(NSString *)identifier
{
return [[MyObject objectsDict] objectForKey:identifier];
}
+ (MyObject *)newRefrenceWithId:(NSString *)identifier
{
MyObject *obj;
#synchronized (self) {
obj = [[MyObject objectsDict] objectForKey:identifier];
if (obj == nil) {
obj = [[MyObject alloc] init];
obj.identifier = identifier;
[[MyObject objectsDict] setObject:obj forKey:identifier];
NSLog(#"new instance of MyObject created with id:%#",identifier);
}
}
return obj;
}
+ (MyObject *)newRefrenceWithId:(NSString *)identifier andClassType:(Class)classType
{
MyObject *obj;
#synchronized (self) {
obj = [[MyObject objectsDict] objectForKey:identifier];
if (obj == nil) {
obj = [[MyObject alloc] initWithClassType:classType andId:identifier];
[[MyObject objectsDict] setObject:obj forKey:identifier];
NSLog(#"new instance of MyObject created with id:%# of ClassType :%#",identifier,NSStringFromClass(classType));
}
}
return obj;
}
+ (void)deleteInstance:(NSString *)identifier
{
#synchronized (self) {
[[MyObject objectsDict] removeObjectForKey:identifier];
}
}
+ (void)clearAllMyObjectsFromMap
{
#synchronized (self) {
[[MyObject objectsDict] removeAllObjects];
}
}
Is there a better way to do this? I hear that #synchronized is very CPU expensive but GCD concurrent queues can't be used in class methods...
UPDATE: Where should the global sync queue be .. in init? That's an instance method so I doesn't work there...
You can use GCD:
Create a global "sync queue" using dispatch_once in a free function defined in module scope:
static dispatch_queue_t get_sync_queue() {
static dispatch_once_t onceToken;
static dispatch_queue_t sync_queue;
dispatch_once(&onceToken, ^{
sync_queue = dispatch_queue_create("my.sync_queue", DISPATCH_QUEUE_CONCURRENT);
});
return sync_queue;
}
Use this queue with dispatch_sync and the block modifying your object:
+ (MyObject *)newRefrenceWithId:(NSString *)identifier
{
__block MyObject *obj;
dispatch_barrier_sync(get_sync_queue(), {
obj = [[MyObject objectsDict] objectForKey:identifier];
if (obj == nil) {
obj = [[MyObject alloc] init];
obj.identifier = identifier;
[[MyObject objectsDict] setObject:obj forKey:identifier];
NSLog(#"new instance of MyObject created with id:%#",identifier);
}
});
return obj;
}
Method newRefrenceWithId: now is fully thread-safe.
Edit:
Alternatively, you can also use a concurrent sync_queue and use dispatch_barrier_sync when reading and writing in the block (as in your case)
Use dispatch_barrier_async when writing the object(s).
In case you just need to read and return the shared object's state you should use dispatch_sync - which allows for concurrent reads.

Singeleton - error: [CFString isNSString__] message sent to deallocated instance

I have an NSMutableArray in a singleton, which I want to access in two classes. I've done this in another project exactly like this (that one had ARC, this one doesn't) and it worked there.
Project doesn't have ARC on.
I'm getting the error:
*** -[CFString isNSString__]: message sent to deallocated instance 0xb3d1db0
StoreVars.h
#interface StoreVars : NSObject
#property (nonatomic, retain) NSMutableArray *sharedArrayOfVideo;
+ (StoreVars *)sharedInstance;
#end
StoreVars.m
#implementation StoreVars
#synthesize sharedArrayOfVideo;
+ (StoreVars *) sharedInstance {
static StoreVars *myInstance = nil;
if (myInstance == nil) {
myInstance = [[[[self class] alloc] init] retain];
myInstance.sharedArrayOfVideo = [[NSMutableArray alloc] init];
}
return myInstance;
}
#end
Populating the array asynchronously:
NSDictionary *tempDict = [[NSDictionary alloc] initWithObjectsAndKeys:
ID,#"id",
title,#"title", nil];
[[StoreVars sharedInstance].sharedArrayOfVideo addObject:tempDict];
This is where crash happens:
NSLog(#"%i",[[StoreVars sharedInstance].sharedArrayOfVideo count]);
NSLog(#"%#",[StoreVars sharedInstance]);
if ([[StoreVars sharedInstance] respondsToSelector:#selector(sharedArrayOfVideo)]) {
**NSLog(#"%#",[StoreVars sharedInstance].sharedArrayOfVideo);**
//NSLog(#"%#",[[StoreVars sharedInstance].sharedArrayOfVideo objectAtIndex:8]);
}
Output:
10
<StoreVars: 0xb381b00>
*** -[CFString isNSString__]: message sent to deallocated instance 0xb3d1db0
Found the problem, when creating the dictionary, I did:
NSString *ID = [[[arrayOfEntry objectAtIndex:index] objectForKey:#"id"]; autorelease]
NSString *title = [[[arrayOfEntry objectAtIndex:index] objectForKey:#"title"] autorelease];
Instead of:
NSString *ID = [[arrayOfEntry objectAtIndex:index] objectForKey:#"id"];
NSString *title = [[arrayOfEntry objectAtIndex:index] objectForKey:#"title"];
NSDictionary *tempDict = [NSDictionary dictionaryWithObjectsAndKeys:ID,#"id",title,#"title", nil];
Try to create singleton with gcd like this:
static ASBTLEManager *sharedInstance = nil;
+ (ASBTLEManager *)sharedManager
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[ASBTLEManager alloc] initInstance];
});
return sharedInstance;
}
try this way to create singleton instance, this is more accurate than what you are trying
+(StoreVars *) sharedInstance
{
static StoreVars * myInstance = nil;
static dispatch_once_t oneTimePredicate;
//get calls only once.
dispatch_once(&oneTimePredicate, ^{
myInstance = [[self alloc] init];
myInstance.sharedArrayOfVideo = [[NSMutableArray alloc] init];
});
return myInstance;
}
I have a question with regards to the line below in your code:
if ([[StoreVars sharedInstance] respondsToSelector:#selector(sharedArrayOfVideo)])
Q. Why have you tried to check the mutableArray as method via selector ?
If you want to iterate through the added dictionaries do the following
StoreVars *storeVars = [StoreVars sharedInstance];
if(storeVars.sharedArrayOfVideo.count>0)
{
for(int i=0; i<storeVars.sharedArrayOfVideo.count; i++)
{
NSDictionary *dictionary = [storeVars.sharedArrayOfVideo objectAtIndex:i];
// do stuff with the grabbed dictionary.
}
}
else { NSLog(#"sharedArrayOfVideo is empty"); }
Further in your singleton creation code perform the following edit:
Instead of following:
if (myInstance == nil) {
myInstance = [[[[self class] alloc] init] retain];
Use the following:
if (myInstance == nil) {
myInstance = [[StoreVars alloc] init];
// you already own the above object via alloc/init, so no extra retains.

Double #synchronized: is it needed?

I'm developing an iOS application with latest SDK.
My app is a port from an Android application and I have these two methods:
- (MyObject*)getMyObject:(MyObjectType)myObjectType
{
#synchronized(self)
{
for (int index = 0; index < [myObjects count]; index++)
{
MyObject* myObject = (MyObject*)[myObjects objectAtIndex:index];
if (myObject.Type == myObjectType)
return myObject;
}
return nil;
}
}
- (BOOL)isMyObjectVisible:(MyObjectType)myObjectType
{
#synchronized(self)
{
return ([self getMyObject:myObjectType] != nil);
}
}
I have isMyObjectVisible:, that is #synchronized, calling another #synchronized method.
Is it necessary that isMyObjectVisible: has to be #synchronized?
To answer your first question, no, the double locking is not needed.
You can keep the lock in getMyObject. That protects it. However, there is nothing in isMyObjectVisible other than a call to getMyObject, so there's nothing else to protect in that method.
However, borrrden's comment is not an issue here. You get a recursive lock when using #synchronized, so you can nest synchronized calls like you're doing without a deadlock. There's just no need to, in your case.
Here is an Example of that you need to use double #synchronized :
NSString * str;
str = [[NSString alloc] initWithFormat:#"str"];
-(void)viewDidLoad{
NSString *foo = #"foo";
NSString *bar = #"bar";
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self firstAction:foo];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self secondAction:bar];
});
}
- (void)firstAction:(NSString *)sender {
NSLog(#"firstAction");
#synchronized (self) {
str = sender;
for (int i=0; i<5; i++) {
NSLog(#"first: %#",str);
}
}
}
- (void)secondAction:(NSString *)sender {
NSLog(#"secondAction");
#synchronized (self) {
str = sender;
for (int i=0; i<5; i++) {
NSLog(#"second: %#",str);
}
}
}
(str is static variable )
-Try to run it without the #synchronized (self) and see what will happen.

Resources