View::findViewById() vs Kotlin Extension View Binding

Continuing on from the discussion originally posted in Chapter 2

I just now went through Chapter 8, where I expected this topic to come up again. One change with the 4th Edition was the placement of the EditText TextWatcher from onCreateView() to onStart(), and the explanation here makes sense. However, we are still following the findViewById() model.

I know that the Kotlin Extension does create some overhead, but per the Kotlin documentation

a speedup can be obtained with large view hierarchies due to the caching (which is from some of the overhead - and you can change the caching strategy if you like). Which method you use does dictate where in the fragment life cycle you can access the view.

findViewById()
This method can be used directly in onCreateView() once the view object has been inflated.

override fun onCreateView(...) {
    val view = inflater.inflate(...)
    titleField = view.findViewById(R.id.crime_title) as EditText
    return view
}

From this point forward in the life cycle (until onDestroyView() is called), titleField now holds the EditText view object.

Kotlin Extension
Due to how the call works behind the scenes, we cannot use the Kotlin Extension until we are in onViewCreated() or later in the life cycle.

But from here forward in our life cycle, the implementation on the fragment side would be the same. We would just use crime_title instead of titleField. Plus, we don’t have the extra step of having to create an extra variable, remembering to set it, and cast it.

I know using the extension method works even with destroying/creating the view and detaching/attaching the fragment. I had previously gone through the 3rd edition book translating all the apps to Kotlin and used this method. There was no problem with the caching mechanism behind the scenes. The snippets of the completed CrimeDetailFragment.kt class are below:

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    Log.d(LOG_TAG, "onCreateView() called")
    return inflater.inflate(R.layout.fragment_details, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    Log.d(LOG_TAG, "onViewCreated() called")

    crime_title_edit_text.setText( crime.title )
    crime_title_edit_text.addTextChangedListener(object: TextWatcher {
        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {

        }
        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            crime.title = s?.toString() ?: ""
            callbacks?.onCrimeUpdated(listPosition)
            setResult()
        }
        override fun afterTextChanged(s: Editable?) {

        }
    })

    crime_date_button.text = crime.date.toString()
    crime_date_button.isEnabled = false 
}

Is there a reason to use one method over the other that I am not aware of? Does one scale better when apps become much more complex? Any other thoughts or insight would be appreciated.