如何在Swift中执行一次代码只执行一次?

时间:2015-12-10 13:51:44

标签: swift grand-central-dispatch

我到目前为止看到的答案(123)建议使用GCD的dispatch_once

var token: dispatch_once_t = 0
func test() {
    dispatch_once(&token) {
        print("This is printed only on the first call to test()")
    }
    print("This is printed for each call to test()")
}
test()

输出:

This is printed only on the first call to test()
This is printed for each call to test()

但等一下。 token是一个变量,所以我可以很容易地做到这一点:

var token: dispatch_once_t = 0
func test() {
    dispatch_once(&token) {
        print("This is printed only on the first call to test()")
    }
    print("This is printed for each call to test()")
}
test()

token = 0

test()

输出:

This is printed only on the first call to test()
This is printed for each call to test()
This is printed only on the first call to test()
This is printed for each call to test()

如果我们可以更改dispatch_once的值,那么token是没有用的!将token转换为常量并不简单,因为它需要UnsafeMutablePointer<dispatch_once_t>类型。

那么我们应该放弃Swift中的dispatch_once吗?有一种更安全的方式只执行一次代码吗?

3 个答案:

答案 0 :(得分:20)

一个男人去看了医生,然后说,#34;医生,当我踩到脚上时,它很痛。&#34;。医生回答说,&#34;所以停止这样做&#34;。

如果您故意改变您的调度令牌,那么是 - 您将能够执行两次代码。但是,如果您使用任何方式来防止多次执行的逻辑,您将能够做到这一点。 dispatch_once仍然是确保代码只执行一次的最佳方法,因为它处理初始化和竞争条件下的所有(非常)复杂的极端情况,简单的布尔值不会覆盖。

如果您担心有人可能会意外地重置令牌,您可以将其包装在一个方法中并尽可能明显,因为它可能是后果。像下面这样的东西会将令牌范围扩展到方法,并防止任何人在没有认真努力的情况下更改它:

func willRunOnce() -> () {
    struct TokenContainer {
        static var token : dispatch_once_t = 0
    }

    dispatch_once(&TokenContainer.token) {
        print("This is printed only on the first call")
    }
}

答案 1 :(得分:15)

由闭包初始化的静态属性是懒惰运行的,最多只运行一次,所以这只打印一次,尽管被调用了两次:

Hibernate: select estimateop0_.id_estimateoptions as id_estim1_6_0_, company4_.id_company as id_compa1_2_1_, user5_.id_user as id_user1_9_2_, billnumber1_.id_bill_number as id_bill_1_0_3_, company2_.id_company as id_compa1_2_4_, user3_.id_user as id_user1_9_5_, estimateop0_.estimate_customer_note as estimate2_6_0_, estimateop0_.estimate_model as estimate3_6_0_, estimateop0_.estimate_model_primary_color as estimate4_6_0_, estimateop0_.estimate_model_secondary_color as estimate5_6_0_, estimateop0_.estimate_model_tertiary_color as estimate6_6_0_, estimateop0_.estimate_personal_note as estimate7_6_0_, estimateop0_.estimate_terms as estimate8_6_0_, company4_.bank_account as bank_acc2_2_1_, company4_.fk_id_company_bill as fk_id_c18_2_1_, company4_.city as city3_2_1_, company4_.company_name as company_4_2_1_, company4_.contact_email as contact_5_2_1_, company4_.contact_telephone as contact_6_2_1_, company4_.country as country7_2_1_, company4_.dateformat as dateform8_2_1_, company4_.default_currency as default_9_2_1_, company4_.fk_id_company_estimate_option as fk_id_c19_2_1_, company4_.hide_tax_number_on_estimate as hide_ta10_2_1_, company4_.house_number as house_n11_2_1_, company4_.street as street12_2_1_, company4_.tax_number as tax_num13_2_1_, company4_.tax_number_enabled as tax_num14_2_1_, company4_.website as website15_2_1_, company4_.website_enabled as website16_2_1_, company4_.zip_code as zip_cod17_2_1_, user5_.authority as authorit2_9_2_, user5_.fk_id_company_user as fk_id_co8_9_2_, user5_.email as email3_9_2_, user5_.enabled as enabled4_9_2_, user5_.first_name as first_na5_9_2_, user5_.last_name as last_nam6_9_2_, user5_.password as password7_9_2_, billnumber1_.credit_note_numberprefix as credit_n2_0_3_, billnumber1_.credit_note_numbersuffix as credit_n3_0_3_, billnumber1_.credit_note_start_number as credit_n4_0_3_, billnumber1_.customers_id_on as customer5_0_3_, billnumber1_.estimate_numberprefix as estimate6_0_3_, billnumber1_.estimate_numbersuffix as estimate7_0_3_, billnumber1_.estimate_start_number as estimate8_0_3_, billnumber1_.every_year_new_date_on as every_ye9_0_3_, billnumber1_.invoice_numberprefix as invoice10_0_3_, billnumber1_.invoice_numbersuffix as invoice11_0_3_, billnumber1_.invoice_project_number as invoice12_0_3_, billnumber1_.invoice_start_number as invoice13_0_3_, billnumber1_.month_on as month_o14_0_3_, billnumber1_.project_number_numberprefix as project15_0_3_, billnumber1_.project_number_on as project16_0_3_, billnumber1_.project_number_total_prefix_numbers as project17_0_3_, billnumber1_.total_prefix_numbers as total_p18_0_3_, billnumber1_.year_on as year_on19_0_3_, company2_.bank_account as bank_acc2_2_4_, company2_.fk_id_company_bill as fk_id_c18_2_4_, company2_.city as city3_2_4_, company2_.company_name as company_4_2_4_, company2_.contact_email as contact_5_2_4_, company2_.contact_telephone as contact_6_2_4_, company2_.country as country7_2_4_, company2_.dateformat as dateform8_2_4_, company2_.default_currency as default_9_2_4_, company2_.fk_id_company_estimate_option as fk_id_c19_2_4_, company2_.hide_tax_number_on_estimate as hide_ta10_2_4_, company2_.house_number as house_n11_2_4_, company2_.street as street12_2_4_, company2_.tax_number as tax_num13_2_4_, company2_.tax_number_enabled as tax_num14_2_4_, company2_.website as website15_2_4_, company2_.website_enabled as website16_2_4_, company2_.zip_code as zip_cod17_2_4_, user3_.authority as authorit2_9_5_, user3_.fk_id_company_user as fk_id_co8_9_5_, user3_.email as email3_9_5_, user3_.enabled as enabled4_9_5_, user3_.first_name as first_na5_9_5_, user3_.last_name as last_nam6_9_5_, user3_.password as password7_9_5_ from tbl_estimateoptions estimateop0_ inner join tbl_company company4_ on estimateop0_.id_estimateoptions=company4_.fk_id_company_estimate_option inner join tbl_user user5_ on company4_.id_company=user5_.fk_id_company_user cross join tbl_bill_number billnumber1_ inner join tbl_company company2_ on billnumber1_.id_bill_number=company2_.fk_id_company_bill inner join tbl_user user3_ on company2_.id_company=user3_.fk_id_company_user where user5_.email=? and user3_.email=?
Hibernate: select company0_.id_company as id_compa1_2_2_, company0_.bank_account as bank_acc2_2_2_, company0_.fk_id_company_bill as fk_id_c18_2_2_, company0_.city as city3_2_2_, company0_.company_name as company_4_2_2_, company0_.contact_email as contact_5_2_2_, company0_.contact_telephone as contact_6_2_2_, company0_.country as country7_2_2_, company0_.dateformat as dateform8_2_2_, company0_.default_currency as default_9_2_2_, company0_.fk_id_company_estimate_option as fk_id_c19_2_2_, company0_.hide_tax_number_on_estimate as hide_ta10_2_2_, company0_.house_number as house_n11_2_2_, company0_.street as street12_2_2_, company0_.tax_number as tax_num13_2_2_, company0_.tax_number_enabled as tax_num14_2_2_, company0_.website as website15_2_2_, company0_.website_enabled as website16_2_2_, company0_.zip_code as zip_cod17_2_2_, billnumber1_.id_bill_number as id_bill_1_0_0_, billnumber1_.credit_note_numberprefix as credit_n2_0_0_, billnumber1_.credit_note_numbersuffix as credit_n3_0_0_, billnumber1_.credit_note_start_number as credit_n4_0_0_, billnumber1_.customers_id_on as customer5_0_0_, billnumber1_.estimate_numberprefix as estimate6_0_0_, billnumber1_.estimate_numbersuffix as estimate7_0_0_, billnumber1_.estimate_start_number as estimate8_0_0_, billnumber1_.every_year_new_date_on as every_ye9_0_0_, billnumber1_.invoice_numberprefix as invoice10_0_0_, billnumber1_.invoice_numbersuffix as invoice11_0_0_, billnumber1_.invoice_project_number as invoice12_0_0_, billnumber1_.invoice_start_number as invoice13_0_0_, billnumber1_.month_on as month_o14_0_0_, billnumber1_.project_number_numberprefix as project15_0_0_, billnumber1_.project_number_on as project16_0_0_, billnumber1_.project_number_total_prefix_numbers as project17_0_0_, billnumber1_.total_prefix_numbers as total_p18_0_0_, billnumber1_.year_on as year_on19_0_0_, estimateop2_.id_estimateoptions as id_estim1_6_1_, estimateop2_.estimate_customer_note as estimate2_6_1_, estimateop2_.estimate_model as estimate3_6_1_, estimateop2_.estimate_model_primary_color as estimate4_6_1_, estimateop2_.estimate_model_secondary_color as estimate5_6_1_, estimateop2_.estimate_model_tertiary_color as estimate6_6_1_, estimateop2_.estimate_personal_note as estimate7_6_1_, estimateop2_.estimate_terms as estimate8_6_1_ from tbl_company company0_ left outer join tbl_bill_number billnumber1_ on company0_.fk_id_company_bill=billnumber1_.id_bill_number left outer join tbl_estimateoptions estimateop2_ on company0_.fk_id_company_estimate_option=estimateop2_.id_estimateoptions where company0_.fk_id_company_bill=?

示例运行:

/*
run like:

    swift once.swift
    swift once.swift run

to see both cases
*/
class Once {
    static let run: Void = {
        print("Behold! \(__FUNCTION__) runs!")
        return ()
    }()
}

if Process.arguments.indexOf("run") != nil {
    let _ = Once.run
    let _ = Once.run
    print("Called twice, but only printed \"Behold\" once, as desired.")
} else {
    print("Note how it's run lazily, so you won't see the \"Behold\" text now.")
}

答案 2 :(得分:2)

我认为最好的方法是根据需要懒洋洋地构建资源。斯威夫特让这很容易。

有几种选择。如前所述,您可以使用闭包初始化类型中的静态属性。

但是,最简单的选择是定义一个全局变量(或常量)并用一个闭包初始化它,然后在初始化代码发生一次的任何地方引用该变量:

let resourceInit : Void = {
  print("doing once...")
  // do something once
}()

另一个选择是在函数中包装类型,以便在调用时读取更好。例如:

func doOnce() {
    struct Resource {
        static var resourceInit : Void = {
            print("doing something once...")
        }()
    }

    let _ = Resource.resourceInit
}

您可以根据需要对此进行修改。例如,您可以根据需要使用私有全局和内部或公共函数,而不是使用函数的内部类型。

但是,我认为最好的方法就是确定初始化所需的资源,并将其作为全局或静态属性延迟创建。