Why does dispatch_get_specific not recognize the queue I created? - ios

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?

Related

How to return current method in GCD block?

Here is my code:
- (void)viewDidLoad {
[super viewDidLoad];
[self testGCD];
}
- (void)testGCD {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(#"1");
return;
});
NSLog(#"2");
}
The console printed 1 and 2.
What I want is only to print 1 the first time. I think maybe the return is not returning from the method, but instead from the block.
Is there any way I can return from the current method in this GCD block?
Use a block to get around the problem.
- (void)testGCD {
__block void(^codeBlock)(void) = ^{ NSLog(#"2"); };
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
codeBlock = ^{ NSLog(#"1"); };
});
codeBlock();
}
If you see the definition of dispatch_once then you gonna see that they are using DISPATCH_EXPECT to compare the onceToken. You can also use if (onceToken != -1) but DISPATCH_EXPECT optimises the code by telling the compiler that the probability of onceToken == -1 is much much higher. This is called Branch Prediction
- (void)testGCD {
static dispatch_once_t onceToken;
if (DISPATCH_EXPECT(onceToken, ~0l) != ~0l) {
dispatch_once(&onceToken, ^{
NSLog(#"1");
return;
});
}
else {
NSLog(#"2");
}
}
- (void)testGCD {
static dispatch_once_t onceToken;
__block NSString *text = #"2";
dispatch_once(&onceToken, ^{
text = #"1";
});
NSLog(#"%#", text);
}
The return statement returns from the current enclosing scope, which in your case is the block. You cannot return from the outer enclosing scope.
You can use a simple boolean flag to determine if this is the first time that the code has been executing, with a serial dispatch queue to ensure that it is thread-safe.
Something like:
- (void)testGCD {
static dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL);
static bool firstRun = YES;
dispatch_sync(queue, ^{
if (firstRun) {
NSLog(#"1);
firstRun = NO;
} else {
NSLog(#"2");
}
});
}
By using a serial dispatch queue you ensure that there cannot be concurrent update of firstRun
Block is like a completely separate method. A return in it will only return from the block. What you effectively seem to need is:
- (void)testGCD {
static BOOL didTrigger = NO;
if(didTrigger) {
NSLog(#"2");
}
else {
didTrigger = YES;
NSLog(#"1");
}
}
You might try to have a simple lock in your case but I am not sure how safe it would be in this case:
- (void)testGCD {
static dispatch_once_t onceToken;
static BOOL didInvokeOnceBlock = NO;
static BOOL didPassSkippedBlock = NO;
dispatch_once(&onceToken, ^{
NSLog(#"1");
didInvokeOnceBlock = YES;
});
if(didInvokeOnceBlock && didPassSkippedBlock) {
NSLog(#"2");
}
didPassSkippedBlock = YES;
}
But this still looks like it may have unstable results when multithreading. You might need to run this atomically for the result to be correct. What I see as potential issue is:
Thread A and thread B start executing the same method.
Thread A collects token and unlocks didInvokeOnceBlock
Thread B skips block and unlocks didPassSkippedBlock but skips NSLog(#"2");
Thread A invokes NSLog(#"2");

iOS - [Class classMethod] results in unrecognized selector sent

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.

Using Grand Central Dispatch with GKLeaderboard

I've ran into a problem, I'm currently using GKLeaderboards and using it to fill a model. I'm having no problems fetching the data, my problem occurs when I go to fill the tableView with the data, and it's not done filling the array before its called to fill the TableView. From what i've read i need to use Grand Central Dispatch so that its not loading on the main thread.
Any help would be much appreciated.
+(EILeaderBoardModel *)scoresAndNameFromLeaderBoard
{
static EILeaderBoardModel *leaderBoard = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
leaderBoard = [[EILeaderBoardModel alloc] init];
leaderBoard.highScorePlayerArray = [[self class] GameCenterLeaderBoard];
});
return leaderBoard;
}
+ (NSMutableArray *)GameCenterLeaderBoard
{
NSMutableArray *_highScorePlayer = [NSMutableArray new];
GKLeaderboard *leaderboardRequest = [[GKLeaderboard alloc] init];
leaderboardRequest.playerScope = GKLeaderboardPlayerScopeGlobal;
leaderboardRequest.timeScope = GKLeaderboardTimeScopeAllTime;
leaderboardRequest.range = NSMakeRange(1, 20);
leaderboardRequest.identifier = GameHighscoreIdentifier;
[leaderboardRequest loadScoresWithCompletionHandler: ^(NSArray *scores, NSError *error) {
if (error != nil)
{
// Handle the error.
}
if (scores != nil)
{
for (NSUInteger i = 0; i < scores.count; i++) {
GKScore *score = (GKScore *)scores[i];
[GKPlayer loadPlayersForIdentifiers:#[score.playerID] withCompletionHandler:^(NSArray *players, NSError *error) {
GKPlayer *player = (GKPlayer *)players[0];
[player loadPhotoForSize:GKPhotoSizeSmall withCompletionHandler:^(UIImage *photo, NSError *error) {
if (error != nil) {
}
UIImage *_avatar;
if (photo != nil) {
_avatar = photo;
} else {
_avatar = [UIImage imageNamed:#"unknownPersonImage.png"];
}
EIPlayer *currentPlayer = [EIPlayer nameLabel:player.displayName
scoreLabel:[NSString stringWithFormat:#"%llD",score.value]
avatar:_avatar];
[_highScorePlayer addObject:currentPlayer];
}];
}];
}
}
}];
return _highScorePlayer;
}
dispatch_queue_t queue = dispatch_queue_create("com.yourQueue.company", 0);
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_async(queue, ^{
// Do all your data fetching work here
// also like adding values to arrays.
dispatch_async(main, ^{
// Do all your UI update logic here like updating data into tables.
[self.tableView reloadData];
});
});
hope this helps.

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.

dispatch_queue and return data

I'm trying to write this method that returns an NSArray. My NSMutableArray (friendUsers) adds the objects right, but outside the dispatch_async the array is empty.
I try to add the users in the main queue ( as ashowed) but the array is empty to. Any ideas ? Thanks for all your help.
- (NSArray *)checkUsersInGroup {
NSMutableArray *friendUsers = [[NSMutableArray alloc] init];
dispatch_queue_t checkUSers = dispatch_queue_create("CheckUsers", NULL);
dispatch_async(checkUSers, ^{
NSArray *totalUsers = [VVDataRead lecturaDades];
NSArray *usersToSearch = [_grup objectForKey:#"groupFriends"];
for (NSString *tempUserId in usersToSearch){
for (NSDictionary *user in totalUsers){
NSString *id = [user objectForKey:#"id"];
if ([tempUserId isEqualToString:id])
dispatch_async(dispatch_get_main_queue(), ^{
[friendUsers addObject:user];
});
}
}
});
NSLog(#"people:%#",friendUsers);
return [friendUsers copy];
}
you can use blocks, it can make your life easier in this case.
- (void)checkUsersInGroupWithCompleteBlock:(void(^)(NSMutableArray * resultArray))completeBlock {
NSMutableArray *friendUsers = [[NSMutableArray alloc] init];
dispatch_queue_t checkUSers = dispatch_queue_create("CheckUsers", NULL);
dispatch_async(checkUSers, ^{
NSArray *totalUsers = [VVDataRead lecturaDades];
NSArray *usersToSearch = [_grup objectForKey:#"groupFriends"];
for (NSString *tempUserId in usersToSearch){
for (NSDictionary *user in totalUsers){
NSString *id = [user objectForKey:#"id"];
if ([tempUserId isEqualToString:id])
dispatch_async(dispatch_get_main_queue(), ^{
[friendUsers addObject:user];
});
}
}
// call the complete block with the result when you finished
if (completeBlock) completeBlock(friendUsers);
});
}
...and here is how you can call the method:
- (void)anyMethod {
// ... do whetever you want here before
[self checkUsersInGroupWithCompleteBlock:^(NSMutableArray *resultArray) {
NSLog(#"%#", resultArray);
}];
// ... or after
}
EDITED:
NOTE: here is another possible solution, but in your case it just suspends the main thread (which is definitely bad), so you won't gain anything with this solution but pain on the main thread, but if you are on two background threads, this solution can give a very nice example of synchronisation between the threads.
- (NSArray *)checkUsersInGroup {
NSMutableArray *friendUsers = [[NSMutableArray alloc] init];
// our semaphore is here
dispatch_semaphore_t _semaphore = dispatch_semaphore_create(0);
dispatch_queue_t checkUSers = dispatch_queue_create("CheckUsers", NULL);
dispatch_async(checkUSers, ^{
NSArray *totalUsers = [VVDataRead lecturaDades];
NSArray *usersToSearch = [_grup objectForKey:#"groupFriends"];
for (NSString *tempUserId in usersToSearch){
for (NSDictionary *user in totalUsers){
NSString *id = [user objectForKey:#"id"];
if ([tempUserId isEqualToString:id])
dispatch_async(dispatch_get_main_queue(), ^{
[friendUsers addObject:user];
});
}
}
// the process finished
dispatch_semaphore_signal(_semaphore);
});
// ... we are wainitng for the semaphore's signal
dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(_semaphore);
NSLog(#"people:%#",friendUsers);
return [friendUsers copy];
}
There are number of strategies to solve this, but as your operation happens on a background thread, returning the array isn't one of them. You could use NSNotificationCenter to signal that the task as finished and read the array. i.e.
- (void)checkUsersInGroup {
NSMutableArray *friendUsers = [[NSMutableArray alloc] init];
dispatch_queue_t checkUSers = dispatch_queue_create("CheckUsers", NULL);
dispatch_async(checkUSers, ^{
NSArray *totalUsers = [VVDataRead lecturaDades];
NSArray *usersToSearch = [_grup objectForKey:#"groupFriends"];
for (NSString *tempUserId in usersToSearch){
for (NSDictionary *user in totalUsers){
NSString *id = [user objectForKey:#"id"];
if ([tempUserId isEqualToString:id])
dispatch_async(dispatch_get_main_queue(), ^{
[friendUsers addObject:user];
});
}
}
// Signal background task is finished
// Make sure to add an observer to this notification
[[NSNotificationCenter defaultCenter] postNotificationName:#"friendsAddLiteral"
object:nil];
});
}
//this method will respond to the notification
- (void) onFriendsAdded:(NSNotification*)notif {
//do something on the main thread
}

Resources