I've got stacked on very simple issue. Hasn't been using Objective-C a lot at last time. Could someone help me with:
+(UNAuthorizationStatus) mCheckPermissions {
__block UNAuthorizationStatus oOutput = 0;
UNUserNotificationCenter* oCenter = [UNUserNotificationCenter currentNotificationCenter];
[oCenter getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
oOutput = settings.authorizationStatus;
}];
return oOutput;
}
I need assign the value to oOutput from completion handler. For now it's not assinning value correctly. What am I missing? And please do not reply me something Swift related. The question is about Objective-C.
What you're missing is that getNotificationSettingsWithCompletionHandler is asynchronous.
That means that the "answer" within the block (your settings.authorizationStatus) comes back to you after the entire mCheckPermissions method has finished executing, including the return. The order of execution is like this:
+(UNAuthorizationStatus) mCheckPermissions {
__block UNAuthorizationStatus oOutput = 0;
/* 1 */ UNUserNotificationCenter* oCenter = [UNUserNotificationCenter currentNotificationCenter];
/* 2 */ [oCenter getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
/* 4 */ oOutput = settings.authorizationStatus;
}];
/* 3 */ return oOutput;
}
Therefore it is impossible to return, from the outer method mCheckPermissions, a value that arrives into the block. (Unless you have a time machine in your pocket, so you can dip into the future and find out what the result will be.)
Solution found. Issue closed.
+(UNAuthorizationStatus) mCheckPermissions {
__block UNAuthorizationStatus oOutput = UNAuthorizationStatusNotDetermined;
dispatch_semaphore_t oSemaphore = dispatch_semaphore_create(0);
UNUserNotificationCenter* oCenter = [UNUserNotificationCenter currentNotificationCenter];
[oCenter getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
oOutput = settings.authorizationStatus;
dispatch_semaphore_signal(oSemaphore);
}];
if (![NSThread isMainThread]) {
dispatch_semaphore_wait(oSemaphore,DISPATCH_TIME_FOREVER);
} else {
while (dispatch_semaphore_wait(oSemaphore,DISPATCH_TIME_NOW)) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0]];
}
}
return oOutput;
}
Related
I declared NSBlockOperation with an object declared inside that operation. My app constantly is crashing because of memory issue. Appreciate any hint with a great explanation on this spent several hours still no success.
runtime: Memory Issues - (5 leaked types): 1 instance of NSExactBlockVariable leaked
- (EMUserInfoOperation*)loadingLocalModelOperationWithColor:(EMOutfitColor)outfitColor gender:(EMGender)gender {
__block EMUserInfoOperation* operation = [EMUserInfoOperation blockOperationWithBlock:^{
NSURL* remoteURL = [NSURL URLWithString:self.settings[kEMRemoteUrlKey]];
EMOutfitModel* model = nil;
if (remoteURL == nil) {
model = [[EMDomainDataLoader sharedLoader] loadEmbededOutfitNamed:self.name gender:gender];
} else {
model = [[EMDomainDataLoader sharedLoader] loadCachedOutfitNamed:self.name withVersion:self.version gender:gender];
}
[model syncApplyTextureFromPath:[self texturePathForColor:outfitColor] textureSampler:EMTextureSamplerColor];
NSString *alphaPath = [self texturePathForAlpha];
if(alphaPath.length > 0) {
[model syncApplyTextureFromPath:alphaPath textureSampler:EMTextureSamplerAlpha];
}
operation.userInfo = model;
}];
return operation;
}
I am guessing that your EMUserInfoOperation object has a strong reference to the block that the operation is created with. And this block also has a strong reference to the EMUserInfoOperation object because it captures the operation variable. So you have a retain cycle.
You can have the block only weakly reference the EMUserInfoOperation object by doing the following:
EMUserInfoOperation* operation;
__block __weak typeof(operation) weakOperation;
weakOperation = operation = [EMUserInfoOperation blockOperationWithBlock:^{
typeof(operation) strongOperation = weakOperation;
if (strongOperation) {
// ...
strongOperation.userInfo = model;
}
}];
return operation;
I wanna implement defer in objective-c. And here's my code:
/**
RAII : ABC->~ABC
*/
#interface DeferImpl_ : NSObject
/**
* init with a callback
*
* #return
*/
-(instancetype) initWithCallback:(void(^)())callback;
/**
* a callback
*/
#property(nonatomic, copy) void(^callback)();
#end
/**
* Defer
*
* #param X { statement; statement; ... }
*
* #return
*/
#define DEFER(X) [[DeferImpl_ alloc] initWithCallback:^X]
#define SAFE_INVOKE(x) do{if(x){(x)();}}while(0)
#implementation DeferImpl_
/**
* invoke callback
*/
-(void) dealloc {
SAFE_INVOKE(self.callback);
}
-(instancetype) initWithCallback:(void(^)())callback {
self = [super init];
self.callback = callback;
return self;
}
#end
The implementation is simple and seems easy to use.
But it's buggy!.
Here comes what I feel frustrated.
int main(void)
{
NSInteger count = 0;
DEFER({
NSLog(#"Defer: %#", #(count));
});
count = 123;
NSLog(#"Before defer block!");
return 0;
}
Log is:
2017-01-12 17:31:32.401 test[73724:18571479] Defer: 0
2017-01-12 17:31:32.402 test[73724:18571479] Before defer block!
So is there anyone can tell me why count still 0 in the block?
You have hidden most of your work into macros (not a good programming style!) and your are missing the basic point - DEFER calls the block immediately, before even reaching count = 123.
If you don't assign the result of [DeferImpl_ alloc] to any variable, it will get released immediately, also immediately calling the block.
Your current code is the same as writing directly:
NSInteger count = 0;
NSLog(#"Defer: %#", #(count));
count = 123;
NSLog(#"Before defer block!");
If you change your code to:
NSInteger count = 0;
id x = DEFER({
NSLog(#"Defer: %#", #(count));
});
count = 123;
NSLog(#"Before defer block!");
Your result will be:
Before defer block!
Defer: 0
Now, why the value is still zero? Because blocks capture variables by value. To capture the reference, you will have to add __block:
__block NSInteger count = 0;
id x = DEFER({
NSLog(#"Defer: %#", #(count));
});
count = 123;
NSLog(#"Before defer block!");
Before defer block!
Defer: 123
See Blocks and Variables
I started writing a simple JSON RPC TCP library in Objective C.
I have a method that invokes a RPC Method:
- (void)invokeMethod:(NSString *)method
withParameters:(id)parameters
requestId:(id)requestId
success:(void (^)(id responseObject))success
failure:(void (^)(NSError *error))failure
{
NSAssert(NSClassFromString(#"NSJSONSerialization"), #"NSJSONSerialization not found!");
NSDictionary *requestObject = #{#"jsonrpc": #"2.0",
#"method": method,
#"params": parameters,
#"id": requestId};
NSError *error = nil;
NSData *jsondData = [NSJSONSerialization dataWithJSONObject:requestObject options:0 error:&error];
if (error){
return failure(error);
}
[self->callbacks setObject:#{#"success": success ? [success copy] : [NSNull null],
#"failure": failure ? [failure copy] : [NSNull null]}
forKey:requestId];
NSString *str = [[NSString alloc] initWithData:jsondData encoding:NSUTF8StringEncoding];
NSLog(#"Sending: %#", str);
[self.socket writeData:jsondData withTimeout:-1 tag:1];
}
The class basically represents a TCP connection, when calling the above method, the JSON data is sent with an id over TCP to the server which either returns a success or a failure:
- (void) socket:(GCDAsyncSocket *)sender didReadData:(NSData *)data withTag:(long)tag
{
NSError *error = nil;
[self.socket readDataWithTimeout:-1 tag:2];
// … rpc response parsing code here, removed for simplicity …
// detect if error or success
NSDictionary *cbs = [self->callbacks objectForKey:JSONRPCObjectId];
void(^success)(id resultObject) = [cbs objectForKey:#"success"];
success ? success(JSONRPCObjectResult) : nil;
return;
}
Now, I am unsure how to keep track of the success and failure blocks, currently I am storing them in an NSMutableDict, using the requestId as key. Is it fine to do this or is there a better approach that I should use?
Blocks in objective-c are objects and you can treat the same way as other object, so storing them in NSDictionarys, NSArrays etc is perfectly fine. The only catch is that blocks when initially created exist in the same memory scope as local variable do and so they are no longer valid when the method that the block is defined in returns, just like all other local variables so you have to copy them first, just copy them and put the copy in the collection. There is a block copy function but you can just send them a copy message [myBlock copy];
Quick answer, seeing as you don't have anything workable yet...
This is more than you asked for; so, you'll probably have to pair it down to meet your specific need. Basically, it stores as many blocks as you specify at contiguous memory addresses. Paste this into a header file or somewhere global to the method from which you will call these:
typedef const typeof(id(^)(void)) retained_object;
static id (^retainable_object)(id(^)(void)) = ^ id (id(^object)(void)) {
return ^{
return object();
};
};
typeof (retained_object) *(^(^retain_object)(id (^__strong)(void)))(void) = ^ (id(^retainable_object)(void)) {
typeof(retained_object) * object_address;
object_address = &retainable_object;
typeof(retained_object) * persistent_object = (typeof(retained_object) *)CFBridgingRetain(retainable_object);
return ^ typeof(retained_object) * {
return persistent_object;
};
};
static void (^(^iterator)(const unsigned long))(id(^)(void)) = ^ (const unsigned long object_count) {
id const * retained_objects_ref[object_count];
return ^ (id const * retained_objects_t[]) {
return ^ (id(^object)(void)) {
object();
int index = 0UL;
int * index_t = &index;
for (; (*index_t) < object_count; ((*index_t) = (*index_t) + 1UL)) printf("retained_object: %p\n", (*((id * const)retained_objects_t + (object_count - index)) = retain_object(retainable_object(object()))));
};
}(retained_objects_ref);
};
From some method, add:
iterator(1000)(^ id { return (^{ printf("stored block\n"); }); });
This should store 1,000 blocks at as many unique memory addresses.
I'm having a hard time finding good examples on how to use these functions.
static void * kQueue1Key = "key1";
static void * kQueue2Key = "key2";
dispatch_queue_t queue1 = dispatch_queue_create("com.company.queue1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("com.company.queue2", DISPATCH_QUEUE_SERIAL);
dispatch_queue_set_specific(queue1, kQueue1Key, (void *)kQueue1Key, NULL);
dispatch_queue_set_specific(queue2, kQueue2Key, (void *)kQueue2Key, NULL);
dispatch_sync(queue1, ^{
if(dispatch_get_specific(kQueue1Key))
{
NSLog(#"I'm expecting this line to run (A)");
dispatch_sync(queue2, ^{
NSLog(#"I'm expecting this line to run (B)");
if(dispatch_get_specific(kQueue2Key))
{
if(dispatch_get_specific(kQueue1Key))
{
NSLog(#"I'm expecting this line to run (C)");
}
else
{
[NSException raise:NSInternalInconsistencyException format:#"Should not end up here (C)"];
}
}
else
{
[NSException raise:NSInternalInconsistencyException format:#"Should not end up here (B)"];
}
});
}
else
{
[NSException raise:NSInternalInconsistencyException format:#"Should not end up here (A)"];
}
});
Result
I'm expecting this line to run (A)
I'm expecting this line to run (B)
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Should not end up here (C)'
Is it expected behavior? If I were to dispatch_sync to queue1 since I'm not on the queue I would deadlock. What am I missing?
Oh here, it popped into my head why you're getting what you're getting. Notes in line:
dispatch_sync(queue1, ^{
When you get to this point, the "current queue" is queue1
if(dispatch_get_specific(kQueue1Key))
You're asking the current queue for the value it has for kQueue1Key, you set that earlier, so it gives it back to you.
{
NSLog(#"I'm expecting this line to run (A)");
dispatch_sync(queue2, ^{
When you get to this point, the "current queue" is now queue2
NSLog(#"I'm expecting this line to run (B)");
if(dispatch_get_specific(kQueue2Key))
You're asking the current queue for the value it has for kQueue2Key, you set that earlier, so it gives it back to you.
{
if(dispatch_get_specific(kQueue1Key))
You're now asking the current queue for the value it has for kQueue1Key. Since the current queue is queue2 and you never set a value with kQueue1Key on queue2 you get back NULL.
{
NSLog(#"I'm expecting this line to run (C)");
}
else
{
[NSException raise:NSInternalInconsistencyException format:#"Should not end up here (C)"];
}
The misunderstanding here is that dispatch_get_specific doesn't traverse the stack of nested queues, it traverses the queue targeting lineage. For instance, if you did this instead,
static void * kQueue1Key = (void*)"key1";
static void * kQueue2Key = (void*)"key2";
dispatch_queue_t queue1 = dispatch_queue_create("com.company.queue1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("com.company.queue2", DISPATCH_QUEUE_SERIAL);
dispatch_queue_set_specific(queue1, kQueue1Key, (void *)kQueue1Key, NULL);
dispatch_queue_set_specific(queue2, kQueue2Key, (void *)kQueue2Key, NULL);
// Set Queue2 to target Queue1
dispatch_set_target_queue(queue2, queue1);
dispatch_sync(queue2, ^{
if(dispatch_get_specific(kQueue1Key))
{
NSLog(#"I'm expecting this line to run (A)");
}
else
{
[NSException raise:NSInternalInconsistencyException format:#"Should not end up here (C)"];
}
if(dispatch_get_specific(kQueue2Key))
{
NSLog(#"I'm expecting this line to run (B)");
}
else
{
[NSException raise:NSInternalInconsistencyException format:#"Should not end up here (C)"];
}
});
...the targeting relationship is the one that gets traversed, not the stack relationship. It would be nice if there were something that traversed the stack relationship, but I'm not aware of anything (that you wouldn't have to implement yourself).
As mentioned in my comment, recursive locking using dispatch_sync is, in the general case, not possible due to the possibility of non-default queue targeting. For what it's worth, given/assuming default queue targeting, here is one possible approach:
#import <unordered_set>
#import <pthread.h>
static dispatch_once_t recursiveLockWithDispatchQueueTLSKeyOnceToken;
static pthread_key_t recursiveLockWithDispatchQueueTLSKey;
typedef std::unordered_multiset<const void*> RecursiveLockQueueBag;
static void freeRecursiveLockWithDispatchQueueTLSValue(void* tlsValue)
{
RecursiveLockQueueBag* ms = reinterpret_cast<RecursiveLockQueueBag*>(tlsValue);
if (ms) delete ms;
}
static inline BOOL queueStackCheck(dispatch_queue_t q, BOOL checkAndPushNotPop) // If yes, check and push if not on. If no, pop.
{
dispatch_once(&recursiveLockWithDispatchQueueTLSKeyOnceToken, ^{
pthread_key_create(&recursiveLockWithDispatchQueueTLSKey, freeRecursiveLockWithDispatchQueueTLSValue);
});
RecursiveLockQueueBag* ms = reinterpret_cast<RecursiveLockQueueBag*>(pthread_getspecific(recursiveLockWithDispatchQueueTLSKey));
if (!ms)
{
ms = new RecursiveLockQueueBag();
pthread_setspecific(recursiveLockWithDispatchQueueTLSKey, reinterpret_cast<const void*>(ms));
}
const void* const vpq = reinterpret_cast<const void*>((__bridge const void*)q);
BOOL alreadyOn = NO;
if (checkAndPushNotPop)
{
alreadyOn = (ms->count(vpq) > 0);
if (!alreadyOn)
{
ms->insert(vpq);
}
}
else
{
ms->erase(vpq);
}
return alreadyOn;
}
void dispatch_recursive_sync(dispatch_queue_t queue, dispatch_block_t block)
{
if (queueStackCheck(queue, YES))
{
block();
}
else
{
#try
{
dispatch_sync(queue, block);
}
#finally
{
queueStackCheck(queue, NO);
}
}
}
#implementation MyAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
dispatch_queue_t a = dispatch_queue_create("a", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t b = dispatch_queue_create("b", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t c = dispatch_queue_create("c", DISPATCH_QUEUE_SERIAL);
//dispatch_set_target_queue(a, c);
dispatch_recursive_sync(a, ^{
dispatch_recursive_sync(b, ^{
dispatch_recursive_sync(c, ^{
dispatch_recursive_sync(a, ^{
dispatch_recursive_sync(b, ^{
dispatch_recursive_sync(c, ^{
dispatch_recursive_sync(a, ^{
NSLog(#"got there");
});
});
});
});
});
});
});
}
#end
This is the lowest-overhead implementation I could think of in a few minutes. I used C++ to avoid message sending overhead. It requires that all uses of the queue use this function. This can be useful when there's a private queue protecting internal state of an object (i.e. where the queue is private and therefore guaranteed not to be retargeted, and where you can easily ensure that all consumers of the queue use dispatch_recursive_sync.
I have a questions about blocks in Objective-C.
For example I have this code:
__block int count = 0;
void (^someFunction)(void) = ^(void){
count = 4;
};
count +=2;
What would be the proper way to write the same piece of code so the count will become 6, not 2 ?!
Thank you!
I should probably show the actual code because my previous question was blurry.
EDIT:
__block CMTime lastTime = CMTimeMake(-1, 1);
__block int count = 0;
[_imageGenerator generateCGImagesAsynchronouslyForTimes:stops
completionHandler:^(CMTime requestedTime, CGImageRef image, CMTime actualTime,
AVAssetImageGeneratorResult result, NSError *error)
{
if (result == AVAssetImageGeneratorSucceeded)
{
NSImage *myImage = [[NSImage alloc] initWithCGImage:image size:(NSSize){50.0,50.0}];
[arrOfImages addObject:myImage];
}
if (result == AVAssetImageGeneratorFailed)
{
NSLog(#"Failed with error: %#", [error localizedDescription]);
}
if (result == AVAssetImageGeneratorCancelled)
{
NSLog(#"Canceled");
}
if (arrOfImages.count > 5)
{
NSLog(#"here");
}
count++;
}];
int f = count+1;
after 10 iterations count is 0...why?!?!
You are not executing the block (calling a block someFunctionmight be a misleading thing)
__block int count = 0;
void (^someBlock)(void) = ^{
count = 4;
};
someBlock();
count +=2;
Call block like this:
someFunction();
So that would be:
__block int count = 0;
void (^someFunction)(void) = ^(void){
count = 4;
};
// call block
someFunction();
count +=2;
Look at the name of the method you are calling; generateCGImagesAsynchronouslyForTimes: completionHandler:.
Asynchronously means that it executes in a different thread (likely via a queue and, as #newaccount points, it may likely be re-scheduled for future execution on the current queue/thread) and the method returns immediately. Thus, when you set f=count+1;, the completion block hasn't even been executed yet because none of the image loads in background threads have been completed.
You need to make a call from that completion block back to your code that needs to respond to the completion. i.e.
^() {
....
dispatch_async(dispatch_get_main_queue(), ^{[self heyManAnImageLoadedDude];});
....
}