How to cancel NSBlockOperation - objective-c-blocks

I have a long running loop I want to run in the background with an NSOperation. I'd like to use a block:
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
while(/* not canceled*/){
//do something...
}
}];
The question is, how to I check to see if it's canceled. The block doesn't take any arguments, and operation is nil at the time it's captured by the block. Is there no way to cancel block operations?

Doh. Dear future googlers: of course operation is nil when copied by the block, but it doesn't have to be copied. It can be qualified with __block like so:
//THIS MIGHT LEAK! See the update below.
__block NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
while( ! [operation isCancelled]){
//do something...
}
}];
UPDATE:
Upon further meditation, it occurs to me that this will create a retain cycle under ARC. In ARC, I believe __block storage is retained. If so, we're in trouble, because NSBlockOperation also keeps a strong references to the passed in block, which now has a strong reference to the operation, which has a strong reference to the passed in block, which…
It's a little less elegant, but using an explicit weak reference should break the cycle:
NSBlockOperation *operation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakOperation = operation;
[operation addExecutionBlock:^{
while( ! [weakOperation isCancelled]){
//do something...
}
}];
Anyone that has ideas for a more elegant solution, please comment!

To reinforce jemmons answer. WWDC 2012 session 211 - Building Concurent User Interfaces (33 mins in)
NSOperationQueue* myQueue = [[NSOperationQueue alloc] init];
NSBlockOperation* myOp = [[NSBlockOperation alloc] init];
// Make a weak reference to avoid a retain cycle
__weak NSBlockOperation* myWeakOp = myOp;
[myOp addExecutionBlock:^{
for (int i = 0; i < 10000; i++) {
if ([myWeakOp isCancelled]) break;
precessData(i);
}
}];
[myQueue addOperation:myOp];

With Swift 5, you can create a cancellable BlockOperation with addExecutionBlock(_:). addExecutionBlock(_:) has the following declaration:
func addExecutionBlock(_ block: #escaping () -> Void)
Adds the specified block to the receiver’s list of blocks to perform.
The example below shows how to implement addExecutionBlock(_:):
let blockOperation = BlockOperation()
blockOperation.addExecutionBlock({ [unowned blockOperation] in
for i in 0 ..< 10000 {
if blockOperation.isCancelled {
print("Cancelled")
return // or break
}
print(i)
}
})
Note that, in order to prevent a retain cycle between the BlockOperation instance and its execution block, you have to use a capture list with a weak or unowned reference to blockOperation inside the execution block.
The following Playground code shows how to cancel a BlockOperation subclass instance and check that there is no retain cycle between it and its execution block:
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
class TestBlockOperation: BlockOperation {
deinit {
print("No retain cycle")
}
}
do {
let queue = OperationQueue()
let blockOperation = TestBlockOperation()
blockOperation.addExecutionBlock({ [unowned blockOperation] in
for i in 0 ..< 10000 {
if blockOperation.isCancelled {
print("Cancelled")
return // or break
}
print(i)
}
})
queue.addOperation(blockOperation)
Thread.sleep(forTimeInterval: 0.5)
blockOperation.cancel()
}
This prints:
0
1
2
3
...
Cancelled
No retain cycle

I wanted to have cancellable blocks that my UICollectionViewController could easily cancel once cells were scrolled off the screen. The blocks are not doing network ops, they are doing image operations (resizing, cropping etc). The blocks themselves need to have a reference to check if their op has been cancelled, and none of the other answers (at the time I wrote this) provided that.
Here's what worked for me (Swift 3) - making blocks that take a weak ref to the BlockOperation, then wrapping them in the BlockOperation block itself:
public extension OperationQueue {
func addCancellableBlock(_ block: #escaping (BlockOperation?)->Void) -> BlockOperation {
let op = BlockOperation.init()
weak var opWeak = op
op.addExecutionBlock {
block(opWeak)
}
self.addOperation(op)
return op
}
}
Using it in my UICollectionViewController:
var ops = [IndexPath:Weak<BlockOperation>]()
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
...
ops[indexPath] = Weak(value: DispatchQueues.concurrentQueue.addCancellableBlock({ (op) in
cell.setup(obj: photoObj, cellsize: cellsize)
}))
}
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if let weakOp = ops[indexPath], let op: BlockOperation = weakOp.value {
NSLog("GCV: CANCELLING OP FOR INDEXPATH \(indexPath)")
op.cancel()
}
}
Completing the picture:
class Weak<T: AnyObject> {
weak var value : T?
init (value: T) {
self.value = value
}
}

Related

"Capturing 'self' strongly in this block is likely to lead to a retain cycle" using Reachability

I'm trying to edit a variable inside the Reachability block using Objective-C, this is the code:
- (void)testInternetConnection
{
internetReachableFoo = [Reachability reachabilityWithHostname:#"www.google.com"];
// Internet is reachable
internetReachableFoo.reachableBlock = ^(Reachability*reach)
{
// Update the UI on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Connessione ad Internet disponibile");
checkConnection = YES;
if(!lastConnectionState)
{
lastConnectionState = YES;
if(doItemsDownload)
[self displayChoice];
}
});
};
// Internet is not reachable
internetReachableFoo.unreachableBlock = ^(Reachability*reach)
{
// Update the UI on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Connessione ad Internet non disponibile");
checkConnection = NO;
lastConnectionState = NO;
});
};
[internetReachableFoo startNotifier];
}
Where checkConnection; & lastConnectionState; are 2 bool declared on my #interface;
The problem is that accessing these variables and calling [self displayChoice]; inside this block gives me the warning: Capturing 'self' strongly in this block is likely to lead to a retain cycle
How can I possibly avoid this error?
I tried declaring a WeakSelf and declaring self but I don't know how to do it for the bool variables
Capturing self strongly in a block is not always bad. If a block is being executed right away (UIView animate block for example) there is generally no risk.
The problem arises when self captures a block strongly and the block in turn captures self strongly. In this case self retains the block and the block retains self so neither can be released --> retain cycle!
To avoid this you need to capture self weakly in the block.
__weak typeof(self) = self; // CREATE A WEAK REFERENCE OF SELF
__block BOOL blockDoItemsDownload = doItemsDownload; // USE THIS INSTEAD OF REFERENCING ENVIRONMENT VARIABLE DIRECTLY
__block BOOL blockCheckConnection = checkConnection;
internetReachableFoo.reachableBlock = ^(Reachability*reach)
{
// Update the UI on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Connessione ad Internet disponibile");
blockCheckConnection = YES;
if(!lastConnectionState)
{
lastConnectionState = YES;
if(blockDoItemsDownload) // Use block variable here
[weakSelf displayChoice]; // Use weakSelf in place of self
}
});
};
There is a cocoapod called libextobjc and what it allows you to do is quickly and cleanly weakify and strongify objects.
#weakify(self)
[someblock:^{
#strongify(self)
}];
As long as you handle self you should be ok. I'm not 100% sure the BOOL values aren't an issue but I think you could do something like this:
BOOL x = YES;
#weakify(self, x)
[someblock:^{
#strongify(self, x)
}];

Crash with removeObserver:forKeyPath: in Foundation

I having some problems with the following crash logs retrieved from the "Crashes" section in Xcode. Only few devices are affected by this crash report.
I have analyzed the problem but I guess it's a bug on Apple framework. But I cannot find a way to replicate it.
Here a similar discussion: Help with crash in removeObserver:forKeyPath:.
Any hints?
Thread 0 name: Thread 0 Crashed:
0 Foundation
0x23507591 _NSKeyValueReplaceObservationInfoForObject + 69
(NSKeyValueObserving.m:1166)
1 Foundation
0x23506fe7 -[NSObject(NSKeyValueObserverRegistration)
_removeObserver:forProperty:] + 327 (NSKeyValueObserving.m:1552)
2 Foundation
0x23506b03 -[NSObject(NSKeyValueObserverRegistration) removeObserver:forKeyPath:] + 163 (NSKeyValueObserving.m:1696)
3 Foundation
0x235069a7 -[NSObject(NSKeyValueObserverRegistration)
removeObserver:forKeyPath:context:] + 219 (NSKeyValueObserving.m:1663)
4 ApplicationName
0x0002e233 -[Supervisor removeObjectObserver:forKeyPath:] + 115 (Supervisor.m:344)
where removeObjectObserver:forKeyPath: is
- (void) removeObjectObserver:(id)object forKeyPath:(NSString *)keyPath {
#try {
[object removeObserver:self forKeyPath:keyPath context:PrivateKVOContext];
} #catch (NSException *exception) { }
}
Observers in Objective-C must be used with extra attention: don't add the same observer multiples time to the same object's property, and wrap the removal if there is one :
if ([self observationInfo]) {
#try {
[self removeObserver:self forKeyPath:keyPath];
}
#catch (NSException *exception) {}
}
You are experiencing crashes because you try to remove twice the observer, or you are removing a non-existant observer.
You should add observers this way :
[yourObject addObserver:self forKeyPath:keypath options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionInitial context:nil/yourContext];
EDIT:
You may remove an observer on an already deallocate object, resulting in this crash.
if (object && [self observationInfo]) {
#try {
[self removeObserver:self forKeyPath:keyPath];
}
#catch (NSException *exception) {}
}
Normally you have an ivar to be able to know whether you object's keypath observing at the moment or not. Like #property(...) BOOL textFieldTextObserving;
And your add/remove-observing methods should check this property before adding/removing to avoid of adding/removing observer twice.
You also can use NSDictionary if there are many observing objects and keypaths (to keep #(BOOL) as objects and -identifiers as keys).
Anyway, doing things using #try-exception is not a recommended Objective-C way.
Apple docs says:
"You should not use a try-catch block in place of standard programming checks for Objective-C methods. In the case of an NSArray, for example, you should always check the array’s count to determine the number of items before trying to access an object at a given index. The objectAtIndex: method throws an exception if you make an out-of-bounds request so that you can find the bug in your code early in the development cycle—you should avoid throwing exceptions in an app that you ship to users."
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/ErrorHandling/ErrorHandling.html
It's too late to give an answer, but I'm faced with the same problem. So i decided to write this for other people.
Note:
The main reason of crash is that you try to remove an observer before add.
I have created some extensions that will help you safely remove the observer. Swift 5.
You can now remove it before adding it, without crashing. Be sure you also delete an observer in deinit.
USAGE:
objectToObserve.safeRemoveObserver(self, keyPath: "myDate", context: &myContext)
EXTENSIONS:
extension NSRegularExpression {
convenience init(_ pattern: String) {
do {
try self.init(pattern: pattern)
} catch {
preconditionFailure("Illegal regular expression: \(pattern).")
}
}
func matches(_ string: String) -> Bool {
let range = NSRange(location: 0, length: string.utf16.count)
return firstMatch(in: string, options: [], range: range) != nil
}
}
extension NSObject {
func safeRemoveObserver(_ observer: NSObject, keyPath: String, context: inout Int) {
let result = checkIfAlreadyAdded(keyPath: keyPath, context: &context)
if result {
removeObserver(observer, forKeyPath: keyPath, context: &context)
}
}
fileprivate func address(_ o: UnsafeRawPointer) -> Int {
return Int(bitPattern: o)
}
fileprivate func checkIfAlreadyAdded(keyPath: String, context: inout Int) -> Bool {
guard self.observationInfo != nil else { return false }
let info = Unmanaged<AnyObject>
.fromOpaque(self.observationInfo!)
.takeUnretainedValue()
let contextStr = NSString(format: "%p", address(&context))
let infoStr = info.description ?? ""
let regex = NSRegularExpression("\(keyPath).*[a-z].*\(contextStr)")
let result = regex.matches(infoStr)
return result
}
}

Objective-C : Synchronizing code

I have a simple class with two ivars, a NSMutableArray and a BOOL. Objects of this class are able to shuffle elements in the array when they are sent the startShuffling message. They do so until they receive the stopShuffling message.
To make it work, the startShuffling method set the boolean to YES, and then dispatch the block of code that shuffles (while(self.isShuffling) { //... } on a concurrent queue. The stopShuffling set the boolean to NO, so that the shuffling process will terminate on the next loop turn.
Here is the interface :
#interface MyClass : NSObject <NSCoding> {
#private
NSMutableArray *elements_;
__block BOOL isShuffling_;
}
#property(readonly) BOOL isShuffling;
-(void)startShuffling;
-(void)stopShuffling;
#end
And the implementation :
#implementation MyClass
#synthesize isShuffling = isShuffling_;
-(void)startShuffling {
if(self.isShuffling) {
return;
}
isShuffling_ = YES;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
while(isShuffling_) {
// code that shuffles, one element by turn
NSUInteger elementIndex = arc4random() % [elements_ count];
id c = [[elements_ objectAtIndex:elementIndex] retain];
[elements_ removeObjectAtIndex:elementIndex];
[elements_ insertObject:c atIndex:[elements_ count]];
[c release];
}
});
}
-(void)stopShuffling {
isShuffling_ = NO;
}
#end
My class conforms to NSCoding protocol and I don't want to abort encoding even if the object is shuffling. Instead I want my object to stop shuffling and then encode itself. So I have written this encoding method :
-(void)encodeWithCoder:(NSCoder *)aCoder {
if(self.isShuffling) {
[self stopShuffling];
}
[aCoder encodeObject:elements_ forKey:kIVPCodingKeyMyClassElements];
}
Finally, here my question.
I think it is possible for the encodeObject:forKey: method to get called while the shuffling loop terminates its last turn (maybe I'm wrong ?).
Is there any way I can make encodeObject:forKey: method get called after wait for the shuffling loop last turn to terminate ?
Yes, the the shuffle code may still be running when the encodeObject:forKey: method is called.
In general, you don't want to dispatch some random block off onto a queue that executes for a really long time, potentially forever. You want to break up the work into blocks of work. And there-in lies your answer.
Something like:
- (void)shuffleAndCheck
{
if (stillShuffling) {
dispatch_async(globalConcurrentQueue, ^{
dispatch_apply(shuffleQueue, ^{... shuffle one card code ...});
});
dispatch_async(shuffleQueue, ^{ [self shuffleAndCheck]; });
}
}
- (void) startShuffling
{
if (stillShuffling) return;
stillShuffling = YES;
[self shuffleAndCheck];
}
- (void) stopShuffling
{
stillShuffling = NO;
dispatch_async(shuffleQueue, ^{ ... encode stuff here ... });
}
Or something.

What is the correct way to dealloc a lock in objective-c

I have created an NSObject, for the sole purpose of locking.
My question is, in my dealloc, how do I release(from memory) the lock. I have read that you should not modify the lock-object within synchronized. But In my scenario, I think that between the time since the #synchronized in dealloc completes, and the time that [_lockObject release] is called, another method in a different thread may acquire _lockObject. And hence if [_lockObject release] is called while _lockObject is held, bad things may happen.
What is the correct way to dispose of the lock in my dealloc method?
-(id)initWithImageFileName:(NSString*)imageFileName
{
if (self = [super init])
{
_imageFileName = [imageFileName retain];
_loadBackgroundCalled = NO;
_deallocing = NO;
_lockObject = [[NSObject alloc]init];
}
return self;
}
-(void)dealloc
{
_deallocing = YES;
#synchronized(_lockObject)
{
..... <releases some stuff>
}
[_lockObject release];
_lockObject = nil;
[_imageFileName release];
_imageFileName = nil;
[super dealloc];
}
-(void)loadBackground
{
#synchronized(_lockObject)
{
if (!_deallocing)
{
if (!_loadBackgroundCalled)
{
_loadBackgroundCalled = YES;
....
}
}
}
}
-(void)loadSprite:(XYTexture*)texture
{
#synchronized(_lockObject)
{
....
}
}
-(void)unloadBackground
{
#synchronized(_lockObject)
{
if (!_deallocing)
{
.....
_loadBackgroundCalled = NO;
}
}
}
Your object cannot be deallocated while it's being used by another object or thread. In other words, any thread that calls your object will already hold a reference to said object, and that reference will prevent the object from deallocating.
When your -dealloc method is executed you can (and must) assume that no other objects or threads hold a reference to your object, and you therefore do not have to worry about concurrency.
If -dealloc is called while your object is executing on a separate thread then that would indicate a bug in your application unrelated to this specific object. You do not solve the problem by setting flags like _deallocing, as in your example code.

Manual retain with ARC

Before ARC I had the following code that retains the delegate while an async operation is in progress:
- (void)startAsyncWork
{
[_delegate retain];
// calls executeAsyncWork asynchronously
}
- (void)executeAsyncWork
{
// when finished, calls stopAsyncWork
}
- (void)stopAsyncWork
{
[_delegate release];
}
What is the equivalent to this pattern with ARC?
I have occasionally needed to manually retain and release things (sometimes just for debugging) and came up with the following macros:
#define AntiARCRetain(...) void *retainedThing = (__bridge_retained void *)__VA_ARGS__; retainedThing = retainedThing
#define AntiARCRelease(...) void *retainedThing = (__bridge void *) __VA_ARGS__; id unretainedThing = (__bridge_transfer id)retainedThing; unretainedThing = nil
This works by using the __bridge_retained and __bridge_transfer to cast things to and from (void *) which causes things to be retained, or to create a strong reference without calling retain.
Have fun, but be careful!
Why not just assign your delegate object to a strong ivar for the duration of the asynchronous task?
Or have a local variable in executeAsyncWork
- (void)executeAsyncWork
{
id localCopy = _delegate;
if (localCopy != nil) // since this method is async, the delegate might have gone
{
// do work on local copy
}
}
Something like this:
- (void)startAsyncWork
{
id<YourProtocol> delegate = _delegate;
dispatch_async(/* some queue */, ^{
// do work
[delegate doSomething];
}
}
The block will retain the delegate as long as needed...