How do I stop this NSThread? - ios

I've got a function that gets called when my app enters background mode. I''d like to stop the thread if user re-opens the app. Nothing I'm trying works so far though.
Here's my code so far:
class Neversleep {
private static var callback : (()->Void)?
private static var thread: NSThread?
static func start(callback: ()->Void) {
self.callback = callback
Neversleep.thread = NSThread(target: self, selector: #selector(Neversleep.task), object: nil)
Neversleep.thread?.start()
}
static func stop() {
print("NEVERSLEEP:STOP")
Neversleep.thread?.cancel()
}
#objc static func task() {
while (true)
{
sleep(3);
print("we are still running!")
callback?()
}
}
}
I call Neversleep.start() from app Delegate's DidEnterBackground method.
I'm calling Neversleep.stop() from willEnterForeground...but it's not stopping the thread.
I'm sure I'm missing something obvious here. But what?

Calling cancel on a thread doesn't automatically kill the thread. The actual body of the thread needs to stop whatever it is doing when its thread is cancelled.
Update your task function like this:
#objc static func task() {
while (!NSThread.currentThread.cancelled)
{
sleep(3);
print("we are still running!")
callback?()
}
}
Double check the actual method and property names for currentThread and cancelled. I'm not 100% sure what they are named in Swift 2.
Even with the above, you will likely get one more call to callback after the thread is cancelled due to the sleep. You can fix this with:
#objc static func task() {
while (!NSThread.currentThread.cancelled)
{
sleep(3);
print("we are still running!")
if (!NSThread.currentThread.cancelled) {
callback?()
}
}
}

Related

NSOperation with a delay - is it async or sync?

I'm creating an NSOperation that executes a closure with a delay. The operations are added into a queue, every time before a new operation is added I cancel all existing ones in the queue:
let myOp = SomeOperation { [weak self] in /* do something */ }
queue.cancelAllOperations()
queue.addOperation(myOp)
Operation Code 1
final class SomeOperation: Operation {
private let closure: () -> Void
init(closure: #escaping () -> Void) {
self.closure = closure
}
override func main() {
if isCancelled {
return
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: doSomething)
}
private func doSomething() {
guard isCancelled == false else {
return
}
closure()
}
}
While the above code works, the code below doesn't. In the DispatchQueue closure, self is nil:
Operation Code 2
final class SomeOperation: Operation {
private let closure: () -> Void
init(closure: #escaping () -> Void) {
self.closure = closure
}
override func main() {
if isCancelled {
return
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
guard let self = self else { return }
guard isCancelled == false else { return }
self.closure()
}
}
}
So I'm trying to learn a bit deeper:
In Code 2, self is nil because as soon as DispatchQueue.main.asyncAfter… is called, the main method finishes and the operation is thus released.
Code 1 works because execute: doSomething implicitly captures/retains a self, so even after asyncAfter, self is still there.
So my questions are:
In Apple's doc it says for concurrent operations I should rather be using start, asynchronous, executing, finished, etc. In my case I just need to have a delay, not actually doing anything asynchronous, should I do it in main only or should I do it as an async operation by implementing those methods Apple suggested?
Is my thinking correct that in Code 1 there's a self retained implicitly, which doesn't sound correct and can create retain cycle?
Thanks!
You asked:
In Apple's doc it says for concurrent operations I should rather be using start, asynchronous, executing, finished, etc. In my case I just need to have a delay, not actually doing anything asynchronous, should I do it in main only or should I do it as an async operation by implementing those methods Apple suggested?
First, you are doing something asynchronous. I.e., asyncAfter is asynchronous.
Second, the motivating reason behind Apple’s concurrent operation discussion is that the operation should not finish until the asynchronous task it launched also finishes. You talk about canceling the operation, but that only makes sense if the operation is still running by the time you go to cancel it. This feature, the wrapping the asynchronous task in an object while never blocking a thread, is one of the key reasons we use operations rather than just GCD. It opens the door for all sorts of elegant dependencies between asynchronous tasks (dependencies, cancelation, etc.).
Is my thinking correct that in Code 1 there's a self retained implicitly, which doesn't sound correct and can create retain cycle?
Regarding the strong reference cycle issues, let’s look at your first example. While it is prudent for the creator of the operation to use [weak self] capture list, it should not be required. Good design of the operation (or anything using asynchronously called closures) is to have it release the closure when it is no longer needed:
class SomeOperation2: Operation {
private var closure: (() -> Void)?
init(closure: #escaping () -> Void) {
self.closure = closure
}
override func main() {
if isCancelled {
return
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: doSomething)
}
override func cancel() {
closure = nil
super.cancel()
}
private func doSomething() {
guard !isCancelled else {
return
}
closure?()
closure = nil
}
}
It doesn’t mean that the caller shouldn’t use [weak self] capture list, only that the operation no longer requires it, and will resolve any strong reference cycles when it is done with the closure.
[Note, in the above, I omitted synchronization of the variable, to keep it simple. But you need to synchronize your access to it to ensure thread-safe design.]
But this design begs the question as to why would you would want to keep the asyncAfter scheduled, still firing even after you canceled the operation. It would be better to cancel it, by wrapping the closure in a DispatchWorkItem, which can be canceled, e.g.:
class SomeOperation: Operation {
private var item: DispatchWorkItem!
init(closure: #escaping () -> Void) {
super.init()
item = DispatchWorkItem { [weak self] in
closure()
self?.item = nil
}
}
override func main() {
if isCancelled { return }
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: item)
}
override func cancel() {
item?.cancel()
item = nil
super.cancel()
}
}
Having outlined the memory issues, we should note that this is all probably moot, as you probably should just make this a concurrent operation (with all that custom KVO) as you identified in the documentation. Besides, all this care we’ve put into cancelation logic only applies if the operation is alive until the asynchronous process finishes. So, we will make a concurrent operation. E.g.,
class SomeOperation: AsynchronousOperation {
private var item: DispatchWorkItem!
init(closure: #escaping () -> Void) {
super.init()
item = DispatchWorkItem { [weak self] in
closure()
self?.item = nil
self?.complete()
}
}
override func main() {
if isCancelled { return }
synchronized {
DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: item)
}
}
override func cancel() {
super.cancel()
synchronized {
item?.cancel()
item = nil
}
}
}
The above uses an asynchronous operation base class that (a) performs the necessary KVO notifications; and (b) is thread-safe. Here is one random example of how that could be implemented:
/// Asynchronous Operation base class
///
/// This class performs all of the necessary KVN of `isFinished` and
/// `isExecuting` for a concurrent `NSOperation` subclass. So, to developer
/// a concurrent NSOperation subclass, you instead subclass this class which:
///
/// - must override `main()` with the tasks that initiate the asynchronous task;
///
/// - must call `complete()` function when the asynchronous task is done;
///
/// - optionally, periodically check `self.cancelled` status, performing any clean-up
/// necessary and then ensuring that `complete()` is called; or
/// override `cancel` method, calling `super.cancel()` and then cleaning-up
/// and ensuring `complete()` is called.
public class AsynchronousOperation: Operation {
private let lock = NSLock()
private var _executing: Bool = false
override private(set) public var isExecuting: Bool {
get {
synchronized { _executing }
}
set {
willChangeValue(forKey: #keyPath(isExecuting))
synchronized { _executing = newValue }
didChangeValue(forKey: #keyPath(isExecuting))
}
}
private var _finished: Bool = false
override private(set) public var isFinished: Bool {
get {
synchronized { _finished }
}
set {
willChangeValue(forKey: #keyPath(isFinished))
synchronized { _finished = newValue }
didChangeValue(forKey: #keyPath(isFinished))
}
}
override public var isAsynchronous: Bool { return true }
/// Complete the operation
///
/// This will result in the appropriate KVN of isFinished and isExecuting
public func complete() {
if isExecuting {
isExecuting = false
isFinished = true
}
}
public override func cancel() {
super.cancel()
complete()
}
override public func start() {
if isCancelled {
isFinished = true
return
}
isExecuting = true
main()
}
override public func main() {
fatalError("subclasses must override `main`")
}
public func synchronized<T>(block: () throws -> T) rethrows -> T {
try lock.synchronized { try block() }
}
}
extension NSLocking {
public func synchronized<T>(block: () throws -> T) rethrows -> T {
lock()
defer { unlock() }
return try block()
}
}

Interoperating Async/await, #MainActor and DispatchQueue.main.async

Say I have this code:
class Presenter {
var viewToUpdate: UIView!
func updateUI() {
viewToUpdate.backgroundColor = .red
}
}
class ShinyNewAsyncAwaitClass {
func doAsyncAwaitThing() async {
// make network call or something
}
}
class OtherClassThatICantUpdateToAsyncAwaitYet {
func doOldClosureBasedThing(completion: #escaping () -> Void) {
// make network call or something
completion()
}
}
class TheClassThatUsesAllThisStuff {
var newClass: ShinyNewAsyncAwaitClass!
var oldClass: OtherClassThatICantUpdateToAsyncAwaitYet!
var presenter: Presenter!
func doSomethingWithNewClass() {
Task {
await self.newClass.doAsyncAwaitThing()
// ---->>> What do I do here? <<<<----
await self.presenter.updateUI()
}
}
func doSomethingWithOldClass() {
oldClass.doOldClosureBasedThing {
DispatchQueue.main.async {
self.presenter.updateUI()
}
}
}
func justUpdateTheView() {
self.presenter.updateUI()
}
}
In short, I have three classes. One I can update to async/await, the other I can't, and one that uses both. Both need access to a function that updates UI, both will need to access that function on the main thread.
I saw somewhere I can add #MainActor to the updateUI function, but in cases where I'm already on the main thread and I just want to call updateUI, like in justUpdateTheView I get this error:
Call to main actor-isolated instance method 'updateUI()' in a synchronous nonisolated context
Add '#MainActor' to make instance method 'justUpdateTheView()' part of global actor 'MainActor'
I can't define justUpdateTheView as #MainActor, because we're trying to update our project to the new concurrency stuff slowly and this would cause a chain reaction of changes that need to be made.
What to do for the best? Can I do something like this:
func doSomethingWithNewClass() {
Task {
await self.newClass.doAsyncAwaitThing()
DispatchQueue.main.async {
self.presenter.updateUI()
}
}
}
It compiles, but are there any gotchas to be aware of?
You can do something like this to run the UI code on the MainActor:
func doSomethingWithNewClass() {
Task {
await self.newClass.doAsyncAwaitThing()
await MainActor.run {
self.presenter.updateUI()
}
}
}

How to cancel specific Operation from OperationQueue in swift

There are 3 Operations in my OperationQueue and i'm not able to cancel specific operation from them.
I referred this example but i can't understand it
NSOperationQueue cancel specific operations
This is my code
class myOperation1 : Operation {
override func main() {
print("op1 (🐭) working....")
for i in 1...10 {
print("🐭")
}
}
}
class myOperation2 : Operation {
override func main() {
print("op2 (🐶) working....")
for i in 1...10 {
print("🐶")
}
}
}
class myOperation3 : Operation {
override func main() {
print("op3 (🍉) working....")
for i in 1...10 {
print("🍉")
}
}
}
let op1 = myOperation1()
let op2 = myOperation2()
let op3 = myOperation3()
op1.completionBlock = {
print("op1 (🐭) completed")
}
op2.completionBlock = {
print("op2 (🐶) completed")
}
op3.completionBlock = {
print("op3 (🍉) completed")
}
let opsQue = OperationQueue()
opsQue.addOperations([op1, op2, op3], waitUntilFinished: false)
DispatchQueue.global().asyncAfter(deadline: .now()) {
opsQue.cancelAllOperations()
}
Inshort i want to cancel second operation from operationQueue.
Please guide me.
Thank you
you can call op2.cancel() to cancel the operation, but you need to take additional steps to really stop your operation from running as cancel() only set the isCanceled property to true.
Please check the developer document.
https://developer.apple.com/documentation/foundation/operation/1408418-iscancelled
The default value of this property is false. Calling the cancel() method of this object sets the value of this property to true. Once canceled, an operation must move to the finished state.
Canceling an operation does not actively stop the receiver’s code from executing. An operation object is responsible for calling this method periodically and stopping itself if the method returns true.
You should always check the value of this property before doing any work towards accomplishing the operation’s task, which typically means checking it at the beginning of your custom main() method. It is possible for an operation to be cancelled before it begins executing or at any time while it is executing. Therefore, checking the value at the beginning of your main() method (and periodically throughout that method) lets you exit as quickly as possible when an operation is cancelled.
opsQue.cancelAllOperations() in your code cause removing non-started operations from queue and calls Operation.cancel() for each executing operation, but it only set isCancelled to true. You need to handle it explicitly
class myOperation2 : Operation {
override func main() {
print("op2 (🐶) working....")
for i in 1...10 {
if self.isCancelled { break } // cancelled, so interrupt
print("🐶")
}
}
}
Hope you referred to the documentation for Operation
There are several KVO-Compliant Properties for observe operation.
There is one property isCancelled - read-only
used to check this property before the execution of the operation
like this:
class myOperation2 : Operation {
override func main() {
print("op2 (🐶) working....")
if self.isCancelled {
return
}
for i in 1...10 {
print("🐶")
}
}
}
and for cancelation:
DispatchQueue.global().asyncAfter(deadline: .now()) {
opsQue.operations[1].cancel()
}

UIApplicationSignificantTimeChange notification is not triggered

I want to use the UIApplicationSignificantTimeChange to check when the day has changed and I encapsulated in my own class so I can easy use it in more view controllers:
public final class DayChangedObserver {
private var token: NSObjectProtocol!
public init?(handler: #escaping () -> ()) {
token = NotificationCenter.default.addObserver(forName: NSNotification.Name.UIApplicationSignificantTimeChange, object: self, queue: nil) { _ in
handler()
}
}
deinit {
NotificationCenter.default.removeObserver(token)
}
}
And I call this code from my view controller:
override func viewDidLoad() {
super.viewDidLoad()
_ = DayChangedObserver() {
print("Day has changed")
}
}
I am testing this on my iPhone and I manually change the time. But it seems that it doesn't work using my class.
Is it something wrong with my implementation ? Because it was working when I was using this event in the past (without my own class implementation).
EDIT1:
I seems that deinit is called immediately after, so I am using an instance variable to keep a strong reference and now it's not deinit anymore, but still doesn't work.
object: self change to object: nil,try it.

Function that executes after multiple completion block has finished

I have a funciton that I would like to only execute IF the two completion block has completed (And no way to tell which one to finish first). Below is my attempt that works. However, it is very messy and if I there are three or more completion block that I want to wait for, I would have flags everywhere. I was wondering if there is a prettier way of doing it.
class TestClass: UIViewController {
var blockOneComplete = false
var blockTwoComplete = false
func blockOneDownloadImageDescription(completion:()->Void) {
downloadAsyncWithCompletion {
blockOneComplete = true
if self.blockTwoComplete == true {
self.allDataDownloadCompleted()
} else {
// Do nothing and wait for block Two to complete
}
}
}
func blockTwoDownloadImageData(completion:()->Void) {
downloadAsyncWithCompletion {
blockTwoComplete = true
if self.blockOneComplete == true {
self.allDataDownloadCompleted()
} else {
// Do nothing and wait for block One to complete
}
}
}
func allDataDownloadComplete() {
// Execute this funciton after all Async Download has complete
}
}
-- Update with final result --
Turns out that what was outlined in this website was exactly what I needed
Using dispatch groups to wait for multiple web services
I believe this was not a duplicate of the SO question mentioned in the comment because the final solution included dispatch_group_enter and dispatch_group_leave
The best option is by using dispatch_group
class TestClass: UIViewController {
var group : dispatch_group_t = dispatch_group_create()
override func viewDidLoad() {
super.viewDidLoad()
dispatch_group_notify(group, dispatch_get_main_queue()) {
allDataDownloadComplete()
}
}
func blockOneDownloadImageDescription(completion:()->Void) {
dispatch_group_enter(group)
downloadAsyncWithCompletion {
dispatch_group_leave(group)
}
}
func blockTwoDownloadImageData(completion:()->Void) {
dispatch_group_enter(group)
downloadAsyncWithCompletion {
dispatch_group_leave(group)
}
}
func allDataDownloadComplete() {
// Execute this funciton after all Async Download has complete
}
}
You will need either use dispatch_group or use functional reactive programming library like RxSwift to achieve it if you does not want to manage flags.
However, you can just use one counter flag and just make a function call or use NSNotification if is for another ViewController.
In one of my project, I need to ensure that at least 3 of the 4 completion block is completed before calling some function. I do it something like this:
class TestClass: UIViewController {
var numberOfBlockCompleted = 0
func blockOneDownloadImageDescription(completion:()->Void) {
downloadAsyncWithCompletion {
numberOfBlockCompleted += 1
self.allDataDownloadCompleted()
}
}
func blockTwoDownloadImageData(completion:()->Void) {
downloadAsyncWithCompletion {
numberOfBlockCompleted += 1
self.allDataDownloadCompleted()
}
}
func blockThreeDownloadImageDesc(completion:()->Void) {
downloadAsyncWithCompletion {
numberOfBlockCompleted += 1
self.allDataDownloadCompleted()
}
}
func allDataDownloadComplete() {
if numberOfBlockCompleted == 3 {
//do something
}
}
}
In my opinion, it depend largely on how complex is the app. If is just for one or two part, a flag is good enough. However, if the app depending largely on chaining network calls and fetching from different server that need to wait for one or another to be completed like a live stocks app then a strong knowledge of GCD or using functional reactive programming will make your job easier in the long run.

Resources