সুইচ 3 জিসিডি এপিআই পরিবর্তনের পরে প্রেরণ_অনসেস


85

dispatch_onceভাষার সংস্করণ 3-এ পরিবর্তনের পরে সুইফটে নতুন সিনট্যাক্সটি কী ? পুরাতন সংস্করণটি নিম্নরূপ ছিল।

var token: dispatch_once_t = 0
func test() {
    dispatch_once(&token) {
    }
}

এইগুলি libdispatch এ পরিবর্তন হয়েছিল।



উত্তর উপর ভিত্তি করে stackoverflow.com/a/38311178/1648724 এবং stackoverflow.com/a/39983813/1648724 , আমি একটি CocoaPod নির্মিত এই কাজ করতে: pod 'SwiftDispatchOnce', '~> 1.0'চিয়ার্স। :]
জেআরজি-বিকাশকারী

উত্তর:


70

ডক থেকে :

প্রেরণ
বিনামূল্যে ফাংশন dispatch_once সুইফট আর উপলব্ধ নেই। সুইফ্টে, আপনি অলসভাবে প্রাথমিক গ্লোবাল বা স্থিতিশীল বৈশিষ্ট্যগুলি ব্যবহার করতে পারেন এবং একই থ্রেড-সুরক্ষা পেতে পারেন এবং প্রেরণ_অনস সরবরাহ করা হিসাবে একবার বলা গ্যারান্টি পেতে পারেন। উদাহরণ:

let myGlobal: () = { … global contains initialization in a call to a closure … }()
_ = myGlobal  // using myGlobal will invoke the initialization code only the first time it is used.

4
এটি এমন নয় যে আপনি জানেন না যে সুইফট দ্রুত পরিবর্তন হবে এবং আপনাকে সুইফ্টের সংস্করণগুলির মধ্যে প্রচুর ভাঙা কোড সংশোধন করতে হবে।
আবিজার্ন

4
সবচেয়ে বড় ব্যথা হ'ল তৃতীয় পক্ষের পোড যা সর্বদা সুইফট 3 সামঞ্জস্যপূর্ণ নয়।
টিঙ্কারবেল

4
@ টিঙ্কারবেল তৃতীয় পক্ষের নির্ভরতা প্রবর্তন করার সময় আপনি যে প্রযুক্তিগত debtণ অর্জন করেন এটি আমি সুইফটকে পছন্দ করি তবে বহিরাগত নির্ভরতা আনতে অতিরিক্ত সতর্কতা অবলম্বন করে যা এ কারণে এটি ব্যবহার করে।
ক্রিস ওয়াগনার

17
dispatch_onceপরিষ্কার ছিল। এটি, দুর্ভাগ্যক্রমে, কুশ্রী এবং বিভ্রান্তিকর ..
আলেকজান্দ্রে জি

105

অলস প্রারম্ভিক গ্লোবালগুলি ব্যবহার করে কিছু এক সময় সূচনা করার জন্য এটি বোঝা যায়, অন্য ধরণের ক্ষেত্রে এটি বোধগম্য নয়। এটি সিলেটনের মতো জিনিসের জন্য অলস সূচনাযুক্ত গ্লোবালগুলি ব্যবহার করতে প্রচুর অর্থবোধ করে, এটি একটি সুইজল সেটআপ রক্ষার মতো বিষয়গুলির জন্য খুব বেশি অর্থবোধ করে না।

এখানে প্রেরণ_অনসের একটি সুইফট 3 শৈলীর প্রয়োগ রয়েছে:

public extension DispatchQueue {

    private static var _onceTracker = [String]()

    /**
     Executes a block of code, associated with a unique token, only once.  The code is thread safe and will
     only execute the code once even in the presence of multithreaded calls.

     - parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
     - parameter block: Block to execute once
     */
    public class func once(token: String, block:@noescape(Void)->Void) {
        objc_sync_enter(self); defer { objc_sync_exit(self) }

        if _onceTracker.contains(token) {
            return
        }

        _onceTracker.append(token)
        block()
    }
}

এখানে একটি উদাহরণ ব্যবহার:

DispatchQueue.once(token: "com.vectorform.test") {
    print( "Do This Once!" )
}

বা একটি ইউইউডি ব্যবহার করে

private let _onceToken = NSUUID().uuidString

DispatchQueue.once(token: _onceToken) {
    print( "Do This Once!" )
}

যেহেতু আমরা বর্তমানে সুইফট 2 থেকে 3 এ স্থানান্তরের সময়ে রয়েছি, এখানে সুইফট 2 বাস্তবায়ন উদাহরণ:

public class Dispatch
{
    private static var _onceTokenTracker = [String]()

    /**
     Executes a block of code, associated with a unique token, only once.  The code is thread safe and will
     only execute the code once even in the presence of multithreaded calls.

     - parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
     - parameter block: Block to execute once
     */
    public class func once(token token: String, @noescape block:dispatch_block_t) {
        objc_sync_enter(self); defer { objc_sync_exit(self) }

        if _onceTokenTracker.contains(token) {
            return
        }

        _onceTokenTracker.append(token)
        block()
    }

}

সমাধানের জন্য আপনাকে অনেক ধন্যবাদ। আমি ঠিক একটি সুইজল সেটআপে আটকা পড়ছিলাম। আমি আশা করি সুইফট টিম এই ব্যবহারের ক্ষেত্রে সমাধান করবে।
salman140

4
আপনার কখনই ব্যবহার করা উচিত হবে না objc_sync_enterএবং objc_sync_exitআর।
smat88dd

4
এবং এটা কেন?
টড কানিংহাম

4
পারফরম্যান্সের জন্য আপনার _onceTrackers এর জন্য অ্যারের পরিবর্তে একটি সেট ব্যবহার করা উচিত। এটি ও (এন) থেকে ও (1) সময়ের জটিলতার উন্নতি করে।
ওয়ার্নার আলটিউইচর

4
সুতরাং আপনি একটি পুনরায় ব্যবহারযোগ্য শ্রেণিটি ধরে ধরে ধরেছেন যে এটি এতটা পুনরায় ব্যবহার করা হবে না :-) যদি ও (এন) থেকে ও (1) এর সময়ের জটিলতা হ্রাস করার জন্য কোনও অতিরিক্ত প্রচেষ্টা প্রয়োজন না হয় আপনার সর্বদা এটি আইএমএইচও করা উচিত।
ওয়ার্নার আলটিউইচর

64

টড কানিংহামের উপরের উত্তরটি প্রসারিত করে, আমি একটি অন্য পদ্ধতি যুক্ত করেছি যা ফাইল, ফাংশন এবং লাইন থেকে স্বয়ংক্রিয়ভাবে টোকেন তৈরি করে।

public extension DispatchQueue {
    private static var _onceTracker = [String]()

    public class func once(file: String = #file,
                           function: String = #function,
                           line: Int = #line,
                           block: () -> Void) {
        let token = "\(file):\(function):\(line)"
        once(token: token, block: block)
    }

    /**
     Executes a block of code, associated with a unique token, only once.  The code is thread safe and will
     only execute the code once even in the presence of multithreaded calls.

     - parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
     - parameter block: Block to execute once
     */
    public class func once(token: String,
                           block: () -> Void) {
        objc_sync_enter(self)
        defer { objc_sync_exit(self) }

        guard !_onceTracker.contains(token) else { return }

        _onceTracker.append(token)
        block()
    }
}

সুতরাং কল করা সহজ হতে পারে:

DispatchQueue.once {
    setupUI()
}

এবং আপনি যদি চান তবে একটি টোকেন নির্দিষ্ট করতে পারেন:

DispatchQueue.once(token: "com.hostname.project") {
    setupUI()
}

আমি মনে করি আপনি দুটি মডিউলে একই ফাইল থাকলে আপনি একটি সংঘর্ষ পেতে পারেন। খুব খারাপ নেই#module


এটি কী চলছে সে সম্পর্কে আরও কিছু আলোকপাত করে। ধন্যবাদ
nyxee


থ্যাঙ্কসকে সত্যই সহায়তা করেছে
মঞ্জুনাথ সি। কাদানী

তবে যদি আমি এটি বুঝতে পারি তবে আপনার কোডটি একবারে কার্যকর করা হবে, এমনকি যদি ক্লাসটি সরানো হয় এবং পুনরায় তৈরি করা হয়, তাই না? এটি মূল প্রেরণ_অনসের থেকে আলাদা, যদি আমি এটি মনে করি ঠিক ...
yonivav

4
@ ইউনিভাভ হ্যাঁ আপনি ঠিক বলেছেন। এটি পেতে আপনি ফাইল, লাইন ইত্যাদির সাথে অবজেক্টের মেমরি ঠিকানায়ও পাস করতে পারেন ... ঠিকানাটিকে টোকেনের অংশ করুন Make
ভ্যাপারওয়ারওয়াল্ফ

19

সম্পাদনা করুন

@ ফ্রিজলব এর উত্তর - এই সমাধানটি থ্রেড-সেফ হওয়ার গ্যারান্টিযুক্ত নয়। যদি এটি গুরুত্বপূর্ণ হয় তবে একটি বিকল্প ব্যবহার করা উচিত

সহজ সমাধান

lazy var dispatchOnce : Void  = { // or anyName I choose

    self.title = "Hello Lazy Guy"

    return
}()

ব্যবহার মত

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    _ = dispatchOnce
}

4
এটি মোটেও সহায়তা করে না, কারণ একটি অলস বর্ণ ঘোষণাটি নিয়মিত কোডের সাথে ইনলাইন করা যায় না, এটি স্ট্রাক্ট বা শ্রেণি সংজ্ঞায় থাকতে হবে। এর অর্থ হল প্রেরণপঞ্জির সামগ্রীগুলি উদাহরণের আশেপাশের সুযোগগুলি ক্যাপচার করতে পারে না। উদাহরণস্বরূপ, যদি আপনি এমন কোনও ক্লোজারের ঘোষণা করেন যা এখনও চলছে না, তবে আপনি সেই বন্ধের অভ্যন্তরের
কাঠামোটি

4
Downvoted কারণ এই কোড স্পষ্টভাবে হয়েছে না dispatch_once হিসাবে একই শব্দার্থবিদ্যা। dispatch_once নিশ্চিত করে যে কোডটি একবারে চালানো হয়েছে, আপনি যে কোনও থ্রেড থেকে কল করেছেন । অলস ভার্সের বহু-থ্রেড পরিবেশে অপরিজ্ঞাত আচরণ রয়েছে।
ফ্রিজল্যাব

এই সমাধানে ইন ব্লক কিছু ক্ষেত্রে দু'বার কল করতে চলেছে
পবিত্র

8

আপনি যদি এখনও একটি ব্রিজিং শিরোলেখ যোগ করেন তবে আপনি এটি ব্যবহার করতে পারেন:

typedef dispatch_once_t mxcl_dispatch_once_t;
void mxcl_dispatch_once(mxcl_dispatch_once_t *predicate, dispatch_block_t block);

তারপরে .mকোথাও:

void mxcl_dispatch_once(mxcl_dispatch_once_t *predicate, dispatch_block_t block) {
    dispatch_once(predicate, block);
}

আপনি এখন mxcl_dispatch_onceসুইফ্ট থেকে ব্যবহার করতে সক্ষম হওয়া উচিত ।

এর পরিবর্তে অ্যাপলের পরামর্শ অনুযায়ী আপনার ব্যবহার করা উচিত, তবে আমার কয়েকটি বৈধ ব্যবহার ছিল যেখানে আমার dispatch_onceদুটি ফাংশনে একক টোকেনের প্রয়োজন ছিল এবং অ্যাপল তার পরিবর্তে যা সরবরাহ করে তা কভার করা যায় না।


8

আপনি এই জাতীয় একটি শীর্ষ স্তরের ভেরিয়েবল ফাংশন ঘোষণা করতে পারেন:

private var doOnce: ()->() = {
    /* do some work only once per instance */
    return {}
}()

তারপরে এটিকে যে কোনও জায়গায় কল করুন:

doOnce()

4
অলস ভার্সগুলি ক্লাসে ছেড়ে দেওয়া হয়, সুতরাং এটি একেবারে প্রেরণ_অনসের মতো কাজ করবে না। এটি অন্তর্নিহিত শ্রেণীর প্রতিটি উদাহরণ একবার সম্পাদন করবে। হয় এটিকে শ্রেণীর বাইরে নিয়ে যান [প্রাইভেট ভার ডুওনস: () -> () = {}] বা এটিকে স্ট্যাটিক চিহ্নিত করুন [স্ট্যাটিক প্রাইভেট ভর ডওঁস: () -> () () = {}]
এলি বার্ক

4
একেবারে সঠিক! ধন্যবাদ বেশিরভাগ ক্ষেত্রে আপনার একবারে ক্রিয়া প্রয়োজন।
বোগদান নোভিকভ

4
এটি সত্যিই দুর্দান্ত সমাধান! মার্জিত, সংক্ষিপ্ত এবং স্পষ্ট
বেন লেগজিও

6

সুইফট 3: যারা পুনরায় ব্যবহারযোগ্য ক্লাসগুলি (বা কাঠামো) পছন্দ করেন তাদের জন্য:

public final class /* struct */ DispatchOnce {
   private var lock: OSSpinLock = OS_SPINLOCK_INIT
   private var isInitialized = false
   public /* mutating */ func perform(block: (Void) -> Void) {
      OSSpinLockLock(&lock)
      if !isInitialized {
         block()
         isInitialized = true
      }
      OSSpinLockUnlock(&lock)
   }
}

ব্যবহার:

class MyViewController: UIViewController {

   private let /* var */ setUpOnce = DispatchOnce()

   override func viewWillAppear() {
      super.viewWillAppear()
      setUpOnce.perform {
         // Do some work here
         // ...
      }
   }

}

আপডেট (28 এপ্রিল 2017): ম্যাকোস এসডিকে 10.12-এ যথাযোগ্য হ্রাস সতর্কতার OSSpinLockসাথে প্রতিস্থাপন করা os_unfair_lockহয়েছে।

public final class /* struct */ DispatchOnce {
   private var lock = os_unfair_lock()
   private var isInitialized = false
   public /* mutating */ func perform(block: (Void) -> Void) {
      os_unfair_lock_lock(&lock)
      if !isInitialized {
         block()
         isInitialized = true
      }
      os_unfair_lock_unlock(&lock)
   }
}

আমি একটি বার্তা পেয়েছি যে ওএসএসস্পিনলকটি আইওএস 10.0 এ
অবহিত হয়েছে

4
ধন্যবাদ! উদাহরণ কোড আপডেট হয়েছে। OSSpinLockসঙ্গে প্রতিস্থাপন os_unfair_lock। বিটিডব্লিউ: এখানে একটি ভাল ডাব্লুডাব্লুডিসি ভিডিও রয়েছে Concurrent Programming: বিকাশকারী.এপলস
ভ্লাদ

0

আমি উপরের উত্তরগুলি উন্নতি করে ফলাফল পেয়েছি:

import Foundation
extension DispatchQueue {
    private static var _onceTracker = [AnyHashable]()

    ///only excute once in same file&&func&&line
    public class func onceInLocation(file: String = #file,
                           function: String = #function,
                           line: Int = #line,
                           block: () -> Void) {
        let token = "\(file):\(function):\(line)"
        once(token: token, block: block)
    }

    ///only excute once in same Variable
    public class func onceInVariable(variable:NSObject, block: () -> Void){
        once(token: variable.rawPointer, block: block)
    }
    /**
     Executes a block of code, associated with a unique token, only once.  The code is thread safe and will
     only execute the code once even in the presence of multithreaded calls.

     - parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
     - parameter block: Block to execute once
     */
    public class func once(token: AnyHashable,block: () -> Void) {
        objc_sync_enter(self)
        defer { objc_sync_exit(self) }

        guard !_onceTracker.contains(token) else { return }

        _onceTracker.append(token)
        block()
    }

}

extension NSObject {
    public var rawPointer:UnsafeMutableRawPointer? {
        get {
            Unmanaged.passUnretained(self).toOpaque()
        }
    }
}

এটি কীভাবে ব্যবহার করবেন সে সম্পর্কে উদাহরণ যুক্ত করুন।
সত্যম

-3

আপনি যদি পূর্ববর্তী সংস্করণগুলি সমর্থন করার প্রয়োজন হয় তবে আপনি যদি সুইফট ১.২ বা তার বেশি বা তার উপরে নেস্টেড স্ট্রাক পদ্ধতির ব্যবহার করছেন তবে ক্লাস ধ্রুবক পদ্ধতির ব্যবহার করুন। সুইফটে সিঙ্গলটন প্যাটার্নের অন্বেষণ। নীচের সমস্ত পন্থা অলস সূচনা এবং থ্রেড সুরক্ষা সমর্থন করে। dispatch_once পদ্ধতির সুইফট ৩.০ এ কাজ করা হয় না

পদ্ধতির এ: ক্লাস ধ্রুবক

class SingletonA {

    static let sharedInstance = SingletonA()

    init() {
        println("AAA");
    }

}

পদ্ধতির বি: নেস্টেড স্ট্রাক্ট

class SingletonB {

    class var sharedInstance: SingletonB {
        struct Static {
            static let instance: SingletonB = SingletonB()
        }
        return Static.instance
    }

}

পদ্ধতির সি: প্রেরণ_অনসেস

class SingletonC {

    class var sharedInstance: SingletonC {
        struct Static {
            static var onceToken: dispatch_once_t = 0
            static var instance: SingletonC? = nil
        }
        dispatch_once(&Static.onceToken) {
            Static.instance = SingletonC()
        }
        return Static.instance!
    }
}

4
প্রশ্নটি বিশেষত সুইফট 3-এর সমাধান সম্পর্কে জিজ্ঞাসা করেছিল
থিমস্মিরাইন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.