Zenject框架(十)

安裝器的運(yùn)行時(shí)參數(shù)(Runtime Parameters For Installers)

通常在從安裝器調(diào)用其他安裝器時(shí),希望能夠傳遞參數(shù)。您可以通過將通用參數(shù)添加到正在使用的任何安裝器基類以及運(yùn)行時(shí)參數(shù)的類型來執(zhí)行此操作。例如,使用非MonoBehaviour安裝器時(shí):

public class FooInstaller : Installer<string, FooInstaller>
{
    string _value;

    public FooInstaller(string value)
    {
        _value = value;
    }

    public override void InstallBindings()
    {
        ...

        Container.BindInstance(_value).WhenInjectedInto<Foo>();
    }
}

public class MainInstaller : MonoInstaller
{
    public override void InstallBindings()
    {
        FooInstaller.Install(Container, "asdf");
    }
}

或者在使用MonoInstaller預(yù)制件時(shí):

public class FooInstaller : MonoInstaller<string, FooInstaller>
{
    string _value;

    // 注意這種情況下我們不能使用構(gòu)造函數(shù)
    [Inject]
    public void Construct(string value)
    {
        _value = value;
    }

    public override void InstallBindings()
    {
        ...

        Container.BindInstance(_value).WhenInjectedInto<Foo>();
    }
}

public class MainInstaller : MonoInstaller
{
    public override void InstallBindings()
    {
        // 為使其正常工作,必須在存在以下預(yù)設(shè)體`Resources / My / Custom / ResourcePath.prefab`。
        //且該預(yù)設(shè)體上有FooInstaller組件
        FooInstaller.InstallFromResource("My/Custom/ResourcePath", Container, "asdf")

        // 如果未提供資源路徑,則假定它存在于資源路徑 
        // 'Resources/Installers/FooInstaller'下
        // 比如:
        // FooInstaller.InstallFromResource(Container, "asdf");
    }
}

ScriptableObjectInstallerMonoInstaller此方面的工作方式相同。

在Unity外部或者Dll中使用Zenject

如果您正在構(gòu)建一些代碼作為DLL然后將它們包含在Unity中,您仍然可以在安裝器中為這些類添加綁定,唯一的限制是您必須使用構(gòu)造函數(shù)注入。如果您想使用其他注入方法,例如成員注入或方法注入,那么您也可以這樣做,但是在這種情況下,您需要為項(xiàng)目添加一個(gè)Zenject-Usage.dll,該文件可以在Zenject\Source\Usage目錄中找到。此DLL還包括標(biāo)準(zhǔn)接口ITickable, IInitializable等,因此您也可以使用它們。

您還可以通過下載Zenject-NonUnity.zip在非Unity項(xiàng)目中使用zenject

最后,如果您嘗試使用Unity生成的解決方案在Unity外部運(yùn)行單元測試,在運(yùn)行時(shí),在Zenject代碼嘗試訪問Unity API時(shí)中可能會(huì)遇到錯(cuò)誤。您可以通過在生成的解決方案中添加ZEN_TESTS_OUTSIDE_UNITY條件編譯來禁用此行為。

Zenject設(shè)置

可以通過ProjectContext上的settings屬性自定義Zenject中的許多默認(rèn)行為。這包括以下內(nèi)容:

  • Validation Error Response(驗(yàn)證錯(cuò)誤響應(yīng)) - 此值控制zenject遇到驗(yàn)證錯(cuò)誤時(shí)觸發(fā)的行為。它可以設(shè)置為“Log”或“Throw”。這里的區(qū)別在于,當(dāng)設(shè)置為“Log”時(shí),每次運(yùn)行驗(yàn)證時(shí)都會(huì)打印多個(gè)驗(yàn)證錯(cuò)誤,而如果設(shè)置為“Throw”,則只會(huì)將第一個(gè)驗(yàn)證錯(cuò)誤輸出到控制臺。取消設(shè)置時(shí),默認(rèn)值為“Log”。如果在單元測試中運(yùn)行驗(yàn)證,“Throw”有時(shí)也很有用。
  • Validation Root Resolve Method (驗(yàn)證根解析方法) - 當(dāng)為給定場景觸發(fā)驗(yàn)證時(shí),DiContainer將執(zhí)行“干運(yùn)行”并假裝實(shí)例化場景中安裝程序定義的整個(gè)對象圖。但是,默認(rèn)情況下,它只會(huì)驗(yàn)證對象圖的“根” - 即“NonLazy”綁定或注入“NonLazy”綁定的綁定。作為選項(xiàng),您可以將此行為更改為“全部”,這將驗(yàn)證所有綁定,甚至是那些當(dāng)前未使用的綁定。
  • Display Warning When Resolving During Install(在安裝期間解析時(shí)顯示警告) - 此值將控制在安裝階段觸發(fā)Resolve或Instantiate時(shí)是否向控制臺發(fā)出警告,如下所示:
Zenject Warning: It is bad practice to call Inject/Resolve/Instantiate before all the Installers have completed!  This is important to ensure that all bindings have properly been installed in case they are needed when injecting/instantiating/resolving.  Detected when operating on type 'Foo'.

因此,如果您經(jīng)常遇到此警告并且意識到您正在執(zhí)行的操作的含義,那么您可以將此值設(shè)置為false以禁止它。

  • Ensure Deterministic Destruction Order On Application Quit(確認(rèn)應(yīng)用程序退出時(shí)的確定性銷毀順序) - 設(shè)置為true時(shí),這將確認(rèn)在應(yīng)用程序關(guān)閉時(shí)以可預(yù)測的順序銷毀所有GameObject和IDisposable。默認(rèn)情況下,它設(shè)置為false,因?yàn)槿绫竟?jié)所述,啟用此功能會(huì)產(chǎn)生一些不良后果。

信號(Signals)

詳見“Zenject框架(十八)- 信號”章節(jié)

使用工廠動(dòng)態(tài)創(chuàng)建對象(Creating Objects Dynamically Using Factories)

詳見后續(xù)信號章節(jié)

內(nèi)存池(Memory Pools)

詳見后續(xù)信號章節(jié)

更新/初始化順序(Update / Initialization Order)

在許多情況下,特別是對于小型項(xiàng)目,類更新或初始化的順序無關(guān)緊要。但是,在較大的項(xiàng)目中,更新或初始化順序要認(rèn)真對待。這在Unity中尤其明顯,因?yàn)樗茈y預(yù)知多個(gè)Start(),Awake()或Update()以何種順序被調(diào)用。不幸的是,Unity沒有一種簡單的方法控制這種情況(除了Edit -> Project Settings -> Script Execution Order,雖然這用起來可能很尷尬)

在Zenject中,默認(rèn)情況下,多個(gè)ITickable和IInitializable按照添加順序進(jìn)行調(diào)用,但是對于對更新或初始化順序有要求的情況,還有另一種方法有時(shí)更好:通過在安裝器中明確指定其優(yōu)先級。例如,在示例項(xiàng)目中,您可以在場景安裝器中找到此代碼:

public class AsteroidsInstaller : MonoInstaller
{
    ...

    void InitExecutionOrder()
    {
        // 很多情況下不需要關(guān)心執(zhí)行順序
        // 但在另一些情況下執(zhí)行順序很重要
        // 例如,我們要求AsteroidManager.Initialize
        // 總是在GameController.Initialize(以及Tick)之前調(diào)用
        // 我們可以這樣做:
        Container.BindExecutionOrder<AsteroidManager>(-10);
        Container.BindExecutionOrder<GameController>(-20);

        // 注意銷毀時(shí)以相反順序進(jìn)行
    }

    ...

    public override void InstallBindings()
    {
        ...
        InitExecutionOrder();
        ...
    }

}

這樣,就不會(huì)因?yàn)椴豢深A(yù)知的依賴項(xiàng)順序?qū)е洛e(cuò)誤發(fā)生。

請注意,給定BindExecutionOrder的值將適用于ITickable/ IInitializable和IDisposable(對于IDisposable為反向順序)。

您還可以分別為每個(gè)特定接口分配優(yōu)先級,如下所示:

Container.BindInitializableExecutionOrder<Foo>(-10);
Container.BindInitializableExecutionOrder<Bar>(-20);

Container.BindTickableExecutionOrder<Foo>(10);
Container.BindTickableExecutionOrder<Bar>(-80);

任何未分配優(yōu)先級的ITickables,IInitializable或IDisposable都會(huì)自動(dòng)優(yōu)先為零。這允許您在未指定的類之前或之后執(zhí)行具有顯式優(yōu)先級的類。例如,上面的代碼將導(dǎo)致Foo.Initialize在Bar.Initialize之前被調(diào)用。

Zenject運(yùn)行順序

下面是運(yùn)行使用Zenject的場景時(shí)會(huì)發(fā)生什么情況的更詳細(xì)的視圖。完全理解Zenject的工作原理可能很有用。

  • Unity Awake()階段開始
    • 調(diào)用SceneContext.Awake()方法。這應(yīng)該始終是場景中執(zhí)行的第一件事。默認(rèn)情況下它也以這種方式工作(如果出現(xiàn)異常,參見“一般指南/建議/陷阱/提示和技巧”最后一條)。
    • 項(xiàng)目上下文(Project Context)初始化。請注意,每次運(yùn)行都只會(huì)發(fā)生一次。如果前一個(gè)場景已初始化ProjectContext,則跳過此步驟。
      • ProjectContext預(yù)制體上的所有可注射的MonoBehaviour都通過DiContainer.QueueForInject傳遞給容器
      • ProjectContext遍歷通過Unity檢視面板添加到其預(yù)制體的所有安裝器(installer),運(yùn)行它們的注入,然后在每個(gè)安裝器上調(diào)用InstallBindings()。每個(gè)安裝器在DiContainer上都會(huì)調(diào)用一些Bind方法。
      • 然后,ProjectContext構(gòu)造所有非延遲的根對象,其中包括從ITickable / IInitializable或IDisposable派生的任何類,以及使用NonLazy()綁定添加的那些類。
      • 注入通過DiContainer.QueueForInject添加的所有實(shí)例
    • 場景上下文(SceneContext)初始化
      • 所有可注入的MonoBehaviour都通過DiContainer.QueueForInject傳遞給場景上下文的容器
      • SceneContext遍歷通過Unity檢視面板添加到其上的所有安裝器(installer),運(yùn)行它們的注入,然后在每個(gè)安裝器上調(diào)用InstallBindings()。每個(gè)安裝器在DiContainer上都會(huì)調(diào)用一些Bind<>方法。
      • 然后,ProjectContext構(gòu)造所有非延遲的根對象,其中包括從ITickable / IInitializable或IDisposable派生的任何類,以及使用NonLazy()綁定添加的那些類。
      • 注入通過DiContainer.QueueForInject添加的所有實(shí)例
    • 如果無法解析某些依賴項(xiàng),zenject拋出異常
    • 場景中其他的Monobehaviour調(diào)用自身的Awake() 方法
  • Unity Start()階段開始
    • 調(diào)用ProjectKernel.Start()方法。這將以在ProjectContext安裝器中指定的順序觸發(fā)所有IInitializable對象的Initialize()方法。
    • 調(diào)用SceneKernel.Start()方法。這將以在SceneContext安裝器中指定的順序觸發(fā)所有IInitializable對象的Initialize()方法。
    • 場景中所有其他的MonoBehaviour調(diào)用自身的Start()方法
  • Unity Update() 階段開始
    • 調(diào)用ProjectKernel.Update()方法,這會(huì)導(dǎo)致為所有ITickable對象調(diào)用Tick()(按照ProjectContext安裝器中指定的順序)
    • 調(diào)用SceneKernel.Update(),導(dǎo)致為所有ITickable對象調(diào)用Tick()(按照SceneContext安裝器中指定的順序)
    • 場景中的所有其他MonoBehaviour調(diào)用自身Update()方法
  • 對LateUpdate和ILateTickable重復(fù)這些相同的步驟
  • 同時(shí),根據(jù)物理時(shí)間步長,對FixedUpdate重復(fù)這些相同的步驟
  • Unity場景被卸載
    • 調(diào)用所有游戲?qū)ο笊舷挛模℅ameObjectContext)中的Dispose()方法
    • 調(diào)用場景上下文(SceneContext)安裝器中的Dispose()方法
  • 退出程序
    • 調(diào)用工程 上下文(ProjectContext)安裝器中的Dispose()方法
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容