This repo demonstrates a crash in the OpenTok iOS SDK when using a custom OTVideoCapture for OTPublisherKit. This specifically applies to ARC-enabled projects (which Swift is automatically), but could also present issues in non-ARC projects.
Note: There is an update to these details, see additional details below.
When setting up OTPublisherKit
to publish to an OTSession
, if you provide your own OTVideoCapture
object, there is a crash when everything is cleaned up and shut down. There is some interesting memory management issues going on here. If you just create the capture device and assign it like this:
func setupCapture() {
let videoCapture = OpenTokVideoCapturer()
publisher?.videoCapture = videoCapture
}
The videoCapture
property on the publisher is actually deallocated immediately, once it is out of the current scope. So, when the publisher tries to do anything with the capture device, it causes a crash trying to reference a deallocated object. You can keep the capture object around, by using a property which holds a strong reference to it, like so:
var videoCapture: OpenTokVideoCapturer? = nil
func setupCapture() {
videoCapture = OpenTokVideoCapturer()
publisher?.videoCapture = videoCapture
}
And this will keep the capture device around for the publisher to use, and keeps it happy for a while. However, ARC magically releases videoCapture
when the parent object is deallocated, and there is also a release somewhere in the publisher when it gets cleaned up as well, causing it to be over-released and a crash by trying to release a deallocated object, yet again.
Pretty much all of the sample code for doing this (including the sample code) that I've seen has been in Objective-C, presumably with ARC not enabled, looks something like this:
publisher.videoCapture = [[OpenTokVideoCapturer alloc] init];
...Which violates the Cocoa Memory Management Policy, because this should look like this:
publisher.videoCapture = [[[OpenTokVideoCapturer alloc] init] autorelease];
In summary, the SDK is over-releasing videoCapture
, and it is just working okay because most code using it is based off of the sample code and happens to leak. So the SDK is accidentally plugging the leak...But this causes a crash on applications that manage memory correctly, or are using ARC.
- Clone this repo.
- Run
pod install
. - Open the workspace and run.
- Tap the go button.
- Wait a few seconds for OpenTok to initialize and connect.
- Tap the back button on the top navigation bar.
- Observe the crash.
For anyone using Swift, I've added an example of a simple hack to workaround this issue to the project. Simply uncomment TokViewController.swift#L77 to see it in action. All it is doing is performing a retain to +1 the reference count without a corresponding release, to match the behavior of the above sample Objective-C code. In any other circumstance, this would result in a memory leak...So be conscious of that if this bug gets fixed in a newer version of the SDK.
There is a fun discussion about this in the OpenTok Support Forums worth perusing. In short, the issue is actually that ARC sees the initCapture
method on the class implementing OTVideoCapture
as a initializer
, and as such, is inserting a release
without a corresponding retain
. This is causing the reference count to get slightly out of whack, since it isn't actually an initializer
.
I don't think this should actually be happening, because according to the ARC docs for what defines an init
method, they must "must return an Objective-C pointer type", and the method certainly doesn't return one. However, Swift initializers do not return a value, which may be why ARC is treating the method this way.
Moral of the story: Don't use init
in your method names, unless it is actually an initializer
.
These are the versions I used for my test, although other versions likely exhibit the same behavior.
- iOS: v9.3
- XCode: v7.3.1
- OpenTok SDK: v2.8.1
- CocoaPods: v1.0.0.rc.2