Swift Bağlamını C İşlevi Geri Aramalarına Geçirme

Apr 28 2023
Projenizde bir C kitaplığıyla arayüz oluşturuyorsanız, Swift kodunun bağlamını C işlevi geri aramaları içinde kullanmak isteyebilirsiniz. Biliyorum, bu yaygın bir senaryo değil.

Projenizde bir C kitaplığıyla arayüz oluşturuyorsanız, Swift kodunun bağlamını C işlevi geri aramaları içinde kullanmak isteyebilirsiniz. Biliyorum, bu yaygın bir senaryo değil. Ancak, bazen düşük seviyeli fonksiyonlarını kullanmak için bir C kütüphanesi ile arayüz oluşturmanız gerekir.

public class Bridge {
  private let ptr: UnsafeMutablePointer<exampleCObject>! // pointer for a C struct

  public func addCallback(outsideFunction: @escaping Int -> Void) {
    ptr.pointee.callback = { argument in
      // how can you use context inside this C callback
      // outsideFunction(argument.inValue) // is it correct?
    }
  }
}

Yukarıdaki kodda, ptr.pointee.callbackörneğin hatırı için bir C işlevi var. Swift'te bir C kitaplığıyla arabirim oluştururken, iki dil arasında veri iletmek için Swift tarafından sağlanan "Güvenli olmayan" türlerin kullanılması yaygın bir durumdur. Bu örnekte kullanılan "UnsafeMutablePointer" türü, hem Swift hem de C'den erişilebilen bir bellek bloğuna işaretçi oluşturmamıza izin veren böyle bir türdür. İşlevimiz, herhangi bir işlev verebilmemiz için argüman olarak bir kapanış alır addCallback. bu imza ile icra edilecektir.

public func addCallback(outsideFunction: @escaping Int -> Void) {
  ptr.pointee.callback = { argument in
    outsideFunction(argument.intValue)
  }
}

Aslında bu sorunun birden fazla çözümü var. Bunlardan biri statik bir değişken kullanmak ve bu işlevi içinde tutmaktır. O zaman bu işlevi C kodu içinde çağırabilirsiniz. selfAncak, bu makalede , C işlevine vermek olan diğer çözümü açıklamak istiyorum .

İlk önce işaret eden bir işaretçi oluşturuyoruz self. Ardından bu işaretçiyi C kütüphanesine veriyoruz.

public class Bridge {
  private let ptr: UnsafeMutablePointer<exampleCObject>! // pointer for a C struct
  private var outsideFunction: (Int -> Void)?

  public func addCallback(outsideFunction: @escaping Int -> Void) {
    self.outsideFunction = outsideFunction
    ptr.pointee.data = UnsafeMutableRawPointer(Unmanaged.passRetained(self).toOpaque()) // 1- create a pointer
    ptr.pointee.callback = { cObject in
      let mySelf = Unmanaged<ExampleClass>.fromOpaque((cObject?.pointee.data)!)
                .takeRetainedValue() // 2- get self from the pointer
      mySelf.outsideFunction(cObject.pointee.counter.intValue) // 3- use self
    }
  }
}

Vurgulanması gereken bazı noktalar var. Kullandık passRetainedve takeRetainedValuefonksiyonlar. passUnretainedve gibi bazı alternatifler vardır takeUnretainedValue.

Bildiğiniz gibi Swift, bir uygulamanın hafızasını yönetmek için Otomatik Referans Saymayı kullanır ve kodunuzun hafıza yönetimini etkilediği için passRetainedve arasında seçim yapmak çok önemlidir. passUnretainedkullanırsanız passRetained, referans sayacını artırmış olursunuz ve kullanmayı bitirdiğinizde onu serbest bırakmak sizin sorumluluğunuzdadır, aksi halde bellek sızıntısına neden olur. takeRetainedValueReferans sayacını azaltmak için kullanmalısınız . Öte yandan, kullanırsanız passUnretained, referans sayacı artmaz, bu da nesnenin siz onu kullanmaya çalışmadan önce yerinin değiştirilebileceği anlamına gelir. Bu nedenle, durumunuza bağlı olarak hangisini kullanacağınız konusunda dikkatli olmanız gerekir. Bellek yönetiminde olası sorunlardan kaçınmak için uygun işlevi seçtiğinizden emin olun.

Sonuç olarak, bağlamı Swift'de C işlevi geri aramalarına geçirmek zorlu bir görev olabilir. Ancak doğru yaklaşımla başarılı bir şekilde gerçekleştirilebilir. Bu makalede, C işlevi içinde erişilmesi gereken sınıf örneğine bir işaretçi kullanmayı içeren bir çözümü inceledik. Bunu yaparak, Swift kullanarak C işlevinin içinde bir kod bloğu çalıştırabildik. Swift'in, Swift ve C arasında veri aktarımı için birkaç "Güvensiz" tür sağladığını unutmamak önemlidir ve ihtiyaçlarınıza göre doğru olanı seçmek çok önemlidir. Ayrıca, bellek sızıntılarını veya çökmeleri önlemek için bu türlerin tutulan ve korunmayan sürümlerini kullanırken dikkatli olmak önemlidir. Bu yönergeleri izleyerek, Swift projenizdeki içeriği C işlevi geri aramalarına etkili bir şekilde iletebilirsiniz.