AVCam Sample Code of Apple is crashing [duplicate] - xcode

I am trying to better understand retain cycles, especially relative to Dispatch Queues. I am working with AVFoundation and managing an AVCaptureSession on a sessionQueue:
private let sessionQueue = DispatchQueue(label: "com.andrewferrarone.sessionQueue")
in a lot of code examples in the apple documentation I see this:
self.sessionQueue.async { [unowned self]
//
}
Is the [unowned self] self here necessary? self (the viewController) references self.sessionQueue and the closure dispatched to self.sessionQueue captures self. Is this a reference cycle? self is not referencing the closure, just the DispatchQueue. If [unowned self] is necessary, then from what I understand, I only want to use unowned self if I am certain that self will not be nil. So lets say I put a task on sessionQueue that takes a long time and the viewController gets popped off and is deallocated before the task finishes? What happens to sessionQueue and the task? If its still around then, when it tries to access self, the app will crash. On the other hand, since unowned self doesn't increment the retain count of self, then it won't prevent the viewController from being deallocated.
So My question is what happens to DispatchQueues when a viewController is deallocated and what happens in this case, if a viewController gets deallocated before a dispatchQueue task is finished? If someone could shed some light on what all is going on here that would be very helpful and appreciated.
Thanks for the help my friends!

Is the [unowned self] self here necessary?
Not only is the use of [unowned self] not necessary, but it's very dangerous in an asynchronously dispatched block. You end up with a dangling pointer to a deallocated object.
If you don't want to keep keep a strong reference to self in an asynchronous call, use [weak self], instead. You should only use unowned if you know the block can never be called after self is deallocated. Obviously, with async call, you don't know this, so [unowned self] should not be used in that context.
Whether you use [weak self] or use strong references is a question of whether you need the asynchronously executed block to keep a strong reference to the object in question or not. For example, if you're updating a view controller's view's controls only, then [weak self] is fine (no point in updating a view that has been dismissed).
The more critical use of weak and unowned references is to avoid strong reference cycles. But that doesn't apply in the example you've provided. You only need to worry about those cycles if the view controller keeps some reference to the blocks itself (e.g. you have some closure property) and those closures reference self, but without a weak/unowned qualifier.
My question is what happens to DispatchQueues when a view controller is deallocated?
Those queues will continue to exist, as will any dispatched blocks, until (a) all dispatched blocks finish; and (b) there are no more strong references to the queue.
So if you asynchronously dispatch blocks with weak references to self (i.e. the view controller), they will continue to run after the view controller is released. This is why it's critical to not use unowned in this context.
For what it's worth, empirical tests can be illuminating. Consider:
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let queue = DispatchQueue(label: "com.domain.app.SecondViewController")
for i in 0 ..< 10 {
queue.async { [weak self] in
print("closure \(i) start")
self?.performSomeTask(i)
print("closure \(i) finish")
}
}
}
private func performSomeTask(_ value: Int) {
print("performSomeTask starting \(value)")
Thread.sleep(forTimeInterval: 5) // you wouldn't generally `sleep`, but merely for diagnostic purposes
print("performSomeTask finishing \(value)")
}
deinit {
print("deinit SecondViewController")
}
}
If you dismiss this view controller while the dispatched blocks are queued up and running, you'll see:
With [weak self], the view controller is retained only until the current dispatched block finishes, the view controller will then be released, and the rest of the blocks will rapidly fire off, but because of [weak self], the performSomeTask won't run after the view controller is dismissed.
If you replace weak with unowned (and obviously remove the ? in self?.performSomeTask(...)), you'll see it crash if you dismiss the view controller before the queued blocks have had a chance to start. This is illustrative of why [unowned self] is so dangerous with asynchronous code.
If you simply remove [weak self] altogether and let it use a implicitly strong reference to self, you'll see it won't deallocate the view controller until all queued blocks finish.

Related

RXSwift act while debounced

I would like to have an Observable that while debounced (in the time frame where we are still not emitting the value) something else will happen, for example show a spinner.
So in my code example I only reference my view AFTER the value is emitted..
observable.debounce(0.3, scheduler: MainScheduler.instance).do(onNext: { spinner in
spinner.stop() //here I set it to stop, I want to run spinner.start() while we are in the debounce area
}).subscribe().disposedBy(disposeBag)
I thought this question might fit my needs but not sure if it is exactly what I ask for:
RxSwift - Debounce/Throttle "inverse"
As far as I understand the question, the goal is to trigger some action for the timeframe of debouncing/throttling.
It is relatively straightforward to do for throttle (i.e. emitting at most once per timeframe): basically use .window() operator, hang the desired action onto it, and then use the result of .window() for actual throttling.
With debounce (i.e. emitting once after upstream did not emit for a given timeframe), it seems to be complex but probably also doable.
It actually does matter whether you are making a network request or using debounce, because a network request would be done using flatMap and is an independent observable. Here is some sample code that will animate an activity indicator while the network request is in flight:
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
let _activityIndicator = activityIndicator! // to avoid dealing with self.
button.rx.tap
.flatMapLatest { () -> Observable<Int> in
let request = Observable<Int>.timer(5.0, scheduler: MainScheduler.instance)
return Observable.using({ ActivityIndicatorAnimator(_activityIndicator) }, observableFactory: { _ in request })
}
.subscribe()
.disposed(by: bag)
}
let bag = DisposeBag()
}
class ActivityIndicatorAnimator: Disposable {
init(_ spinner: UIActivityIndicatorView) {
self.spinner = spinner
spinner.startAnimating()
}
func dispose() {
spinner.stopAnimating()
}
let spinner: UIActivityIndicatorView
}
The above uses a timer to simulate a network request. The using operator will create a resource when the observable starts and dispose of the resource when the observable completes. The ActivityIndicatorAnimator resource starts the animation when the resource is created and stops it when the resource is disposed.
The RxSwift repository has a more complex example called ActivityIndicator that maintains a count of how many times it was started and stopped and can be used to monitor several network requests.

When is a Cocoa binding 'set'

I've got an NSArrayController set in my storyboard where I set the mode to Entity Name with a name of Client, and bound the managed object context, selection indexes, and sort descriptors. My NSPopupButton links to that array controller and when I run I see all the elements I expect on the button.
Now I made a strong #IBOutlet in my code and I'm trying to access the contents:
let objs = clientArrayController.arrangedObjects as! [Client]
print("I have \(objs.count) clients")
I tried that code in viewDidLoad, viewWillAppear and viewDidAppear. They all say 0 clients. Clearly that's not possible as I have the clients showing in the UI.
What am I doing wrong here?
viewDidLoad, viewWillAppear, and viewDidAppear are possibly all called before your array controller collects its data from the core data store - hence they (correctly) report a count of zero. To get word of any changes to your array controller's arrangedObjects array you could use one of the aforementioned methods to install an observer that watches this object and reports any changes:
// MyPopUpController.swift
var ArrayControllerArrangedObjectsObservationContext = "arrayController.arrangedObjects"
func viewDidLoad() {
arrayController.addObserver(self,
forKeyPath: "arrangedObjects",
options: .New | .Old,
context: &ArrayControllerArrangedObjectsObservationContext)
}
You're given the opportunity to respond to changes in observeValueForKeyPath...
// MyPopUpController.swift
override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
switch context {
case &ArrayControllerArrangedObjectsObservationContext:
// Check counts here
default:
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
If you only need the observer on start-up, after you've checked the counts, and done whatever you need to do you should then remove the observer:
// MyPopUpController.swift
arrayController.removeObserver(self,
forKeyPath: "arrangedObjects",
context: &ArrayControllerArrangedObjectsObservationContext)

How to detect if ancestor is focused in tvOS?

I have a custom UIView subclass and want to detect when its ancestor is in focus. But this class itself cannot be focused.
Basically, I want to do something similar to UIImageView when adjustsImageWhenAncestorFocused is set to true.
I'm hoping for a solution that doesn't require the ancestor to communicate when focus changes.
You could implement the didUpdateFocusInContext and check if the target view is your ancestor:
override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator)
{
if let target = context.nextFocusedView
where self.isDescendantOfView(target)
{
// one of your ancestors has received focus
}
}
We can't use didUpdateFocus(in:with:), because it isn't called for all views of hierarchy, from the documentation:
the focus engine calls this method on all focus environments that contain either the previously focused view, the next focused view, or both
So if the view return false for canBecomeFocused, this method can't be called on it.
The adequate way is to observe UIFocusSystem.didUpdateNotification, if you're using Combine it's something like this:
NotificationCenter.default.publisher(for: UIFocusSystem.didUpdateNotification).sink { [weak self] notification in
guard let context = notification.userInfo?[UIFocusSystem.focusUpdateContextUserInfoKey] as? UIFocusUpdateContext,
let next = context.nextFocusedView, self?.isDescendant(of: next) ?? false else {
print("is NOT descendant")
return
}
print("is descendant")
}.store(in: &cancelBag)

How to properly use Cocos2d's update/tick method to trigger an event, only once?

I keep running into this issue wherein I'd like to trigger an event (void) during a scheduled update or tick method - but only trigger it once. The problem is that it gets triggered every time update/tick gets called (each frame). Depending on what method is being called, this slows down the game and occasionally crashes (e.g. addChild already added). I've used a BOOL (e.g. eventTriggered) before to try to handle this situation but am wondering if that is the only and/or best way?
If you're using cocos2d 2.0 just use:
[self scheduleOnce:#selector(yourMethod:) delay:3.0f];
In all other cases simply unschedule the scheduled selector:
-(void) yourScheduledMethodThatShouldOnlyRunOnce:(ccTime)delta
{
[self unschedule:_cmd];
// do stuff once
}
If it's a custom method you need to have some condition that fires the method call, for example:
-(void) update:(ccTime)delta
{
if (runThisNowButOnlyOnce)
{
runThisNowButOnlyOnce = NO;
[self runThisNowButOnlyOnceMethod];
}
}
You just need to figure out when and where to set runThisNowButOnlyOnce to YES. Also don't forget to add it as an ivar to the #interface.

Can I receive a callback whenever an NSPasteboard is written to?

I've read Apple's Pasteboard Programming Guide, but it doesn't answer a particular question I have.
I'm trying to write a Cocoa application (for OS X, not iOS) that will keep track of everything that is written to the general pasteboard (so, whenever any application copies and pastes, but not, say, drags-and-drops, which also makes use of NSPasteboard). I could (almost) accomplish this by basically polling the general pasteboard on a background thread constantly, and checking changeCount. Of course, doing this would make me feel very dirty on the inside.
My question is, is there a way to ask the Pasteboard server to notify me through some sort of callback any time a change is made to the general pasteboard? I couldn't find anything in the NSPasteboard class reference, but I'm hoping it lurks somewhere else.
Another way I could imagine accomplishing this is if there was a way to swap out the general pasteboard implementation with a subclass of NSPasteboard that I could define myself to issue a callback. Maybe something like this is possible?
I would greatly prefer if this were possible with public, App Store-legal APIs, but if using a private API is necessary, I'll take that too.
Thanks!
Unfortunately the only available method is by polling (booo!). There are no notifications and there's nothing to observe for changed pasteboard contents. Check out Apple's ClipboardViewer sample code to see how they deal with inspecting the clipboard. Add a (hopefully not overzealous) timer to keep checking for differences and you've got a basic (if clunky) solution that should be App-Store-Friendly.
File an enhancement request at bugreporter.apple.com to request notifications or some other callback. Unfortunately it wouldn't help you until the next major OS release at the earliest but for now it's polling until we all ask them to give us something better.
There was once a post on a mailing list where the decision against a notification api was described. I can't find it right now though. The bottom line was that probably too many applications would register for that api even though they really wouldn't need to. If you then copy something the whole system goes through the new clipboard content like crazy, creating lots of work for the computer. So i don't think they'll change that behavior anytime soon. The whole NSPasteboard API is internally built around using the changeCount, too. So even your custom subclass of NSPasteboard would still have to keep polling.
If you really want to check if the pasteboard changed, just keep observing the changeCount very half second. Comparing integers is really fast so there's really no performance issue here.
Based on answer provided by Joshua I came up with similar implementation but in swift, here is the link to its gist: PasteboardWatcher.swift
Code snippet from same:
class PasteboardWatcher : NSObject {
// assigning a pasteboard object
private let pasteboard = NSPasteboard.generalPasteboard()
// to keep track of count of objects currently copied
// also helps in determining if a new object is copied
private var changeCount : Int
// used to perform polling to identify if url with desired kind is copied
private var timer: NSTimer?
// the delegate which will be notified when desired link is copied
weak var delegate: PasteboardWatcherDelegate?
// the kinds of files for which if url is copied the delegate is notified
private let fileKinds : [String]
/// initializer which should be used to initialize object of this class
/// - Parameter fileKinds: an array containing the desired file kinds
init(fileKinds: [String]) {
// assigning current pasteboard changeCount so that it can be compared later to identify changes
changeCount = pasteboard.changeCount
// assigning passed desired file kinds to respective instance variable
self.fileKinds = fileKinds
super.init()
}
/// starts polling to identify if url with desired kind is copied
/// - Note: uses an NSTimer for polling
func startPolling () {
// setup and start of timer
timer = NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: Selector("checkForChangesInPasteboard"), userInfo: nil, repeats: true)
}
/// method invoked continuously by timer
/// - Note: To keep this method as private I referred this answer at stackoverflow - [Swift - NSTimer does not invoke a private func as selector](http://stackoverflow.com/a/30947182/217586)
#objc private func checkForChangesInPasteboard() {
// check if there is any new item copied
// also check if kind of copied item is string
if let copiedString = pasteboard.stringForType(NSPasteboardTypeString) where pasteboard.changeCount != changeCount {
// obtain url from copied link if its path extension is one of the desired extensions
if let fileUrl = NSURL(string: copiedString) where self.fileKinds.contains(fileUrl.pathExtension!){
// invoke appropriate method on delegate
self.delegate?.newlyCopiedUrlObtained(copiedUrl: fileUrl)
}
// assign new change count to instance variable for later comparison
changeCount = pasteboard.changeCount
}
}
}
Note: in the shared code I am trying to identify if user has copied a
file url or not, the provided code can easily be modified for other general
purposes.
For those who need simplified version of code snippet that gets the job done in Swift 5.7,
it just works (base on #Devarshi code):
func watch(using closure: #escaping (_ copiedString: String) -> Void) {
let pasteboard = NSPasteboard.general
var changeCount = NSPasteboard.general.changeCount
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
guard let copiedString = pasteboard.string(forType: .string),
pasteboard.changeCount != changeCount else { return }
defer {
changeCount = pasteboard.changeCount
}
closure(copiedString)
}
}
how to use is as below:
watch {
print("detected : \($0)")
}
then if you attempt copy any text in your pasteboard, it will watch and print out to the console like below..
detected : your copied message in pasteboard
detected : your copied message in pasteboard
in case, full code sample for how to use it for example in SwiftUI:
import SwiftUI
#main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.onAppear {
watch {
print("detect : \($0)")
}
}
}
}
func watch(using closure: #escaping (_ copiedString: String) -> Void) {
let pasteboard = NSPasteboard.general
var changeCount = NSPasteboard.general.changeCount
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
guard let copiedString = pasteboard.string(forType: .string),
pasteboard.changeCount != changeCount else { return }
defer {
changeCount = pasteboard.changeCount
}
closure(copiedString)
}
}
}
It's not necessary to poll. Pasteboard would generally only be changed by the current view is inactive or does not have focus. Pasteboard has a counter that is incremented when contents change. When window regains focus (windowDidBecomeKey), check if changeCount has changed then process accordingly.
This does not capture every change, but lets your application respond if the Pasteboard is different when it becomes active.
In Swift...
var pasteboardChangeCount = NSPasteboard.general().changeCount
func windowDidBecomeKey(_ notification: Notification)
{ Swift.print("windowDidBecomeKey")
if pasteboardChangeCount != NSPasteboard.general().changeCount
{ viewController.checkPasteboard()
pasteboardChangeCount = NSPasteboard.general().changeCount
}
}
I have a solution for more strict case: detecting when your content in NSPasteboard was replaced by something else.
If you create a class that conforms to NSPasteboardWriting and pass it to -writeObjects: along with the actual content, NSPasteboard will retain this object until its content is replaced. If there are no other strong references to this object, it get deallocated.
Deallocation of this object is the moment when new NSPasteboard got new content.

Resources