PERF: ensure_string_array with non-numpy input array
Currently, if the input array to `ensure_string_array` is a python object (e.g. an `ExtensionArray`), the function will constantly switch between python and cython level code, which is slow.

This PR fixes that by ensuring we always have a numpy array, avoiding the trips to python level code.

``` python
>>> n = 50_000
>>> cat = pd.Categorical([*['a', pd.NA] * n])
>>> %timeit cat.astype("string")
447 ms ± 11.9 ms per loop  # master
5.43 ms ± 80.5 µs per loop  # this PR
```

xref #35519, #36464.