A Disaster Named NSHashTable🤯

Kiarash Vosough
Divar Mobile Engineering
4 min readSep 10, 2022

Let’s see how to store objects in an array with many options And Many Pitfalls.

Foundation And CoreFoundation Framework

As you may have noticed, there are two frameworks introduced by apple:

  1. CoreFoundation
  2. Foundation

CoreFoundation is a low-level C API, but Foundation is a high-level Objective-C API. I just wanted to give you a hint to follow and learn more. For further reading, follow this link.

Foundation comes with a lot of handy tools. of them, which I intend to talk about, is a class named NSHashTable.

NSHashTable

NSHashTable is modeled after NSSet on Foundation Framework, but it is much more flexible regarding object/memory handling.

Some features of NSHashTable are:

  1. Mutable, without an immutable counterpart.
  2. It can hold weak references to its members.
  3. Can copy members on input.
  4. It can contain arbitrary pointers and use pointer identity for equality and hashing checks.

It also accepts an OptionSet to configure the behavior we desire. Let’s see how it works.

NSPointerFunctions.Options consist of two groups of options:

  1. Memory
  2. Personality
  3. Copy

Memory Options

These options are related to the strategy which NSHashTable uses to hold an object.

  1. strongMemory
  2. opaqueMemory
  3. mallocMemory(read this)
  4. machVirtualMemory(read this)
  5. weakMemory

The most useful which we use are strongMemory and weakMemory. It can aim to store elements’ references as weak or strong.

We skip strongMemory because it is similar to NSSet or NSArray.

Notice that you are obligated to pick only one of the options above.

Personality Options

These Options determine the strategy to be used by NSHashTable to distinguish or identify objects. This could be addressed by using hashValues, exactly mentioned in NSHashTable’s name.

  1. objectPersonality
  2. opaquePersonality
  3. objectPointerPersonality
  4. cStringPersonality
  5. structPersonality
  6. integerPersonality

Notice that you are obligated to pick only one of the options above.

Copy Option

Last but not least, this standalone option creates a copy from an object that can be stored inside an NSHashTable.

Examples

There are some pitfalls in using NSHashTable, so I will go through some examples and explain where and how they should be used.

Here is an example of creating an NSHashTable, which can hold a weak reference to its elements.

Notice that this class already owns a static method to compose NSHashTable with the weakMemory and objectPersonality options.

You can observe the result of the code above; when we assign nil to optionalInstance and wait for 0.3 seconds, the number of items inside the NSHashTable becomes zero.

Pitfalls

Not everything in this world is perfect, NSHashTable is not an exception, but it has some wired pitfalls, which are listed below.

Pitfall Number 1

You might notice why the last print was executed with delay. The reason can be found on Apple’s docs which said

Entries are not necessarily purged right away when the weak object is reclaimed.

That is an inconvenient behavior by NSHashTable, which can be solved using allObjects’s array count, which always returns valid data.

Pitfall Number 2

The count property on the object won’t reflect the changes correctly even if you wait a considerable amount of time.

count returns the number of references currently in the hash table, some of which may be null. That is the reason why the count reports the wrong number.

Pitfall Number 3

The most wired thing happens when you try to store Foundation types like NSString, NSIndexPath, etc. Even if you make the instance optional and nil, they will exist inside NSHashTable objects.

wired right?🫤

I searched a lot across the internet but did not find anything documented. My theory is that the reference of Immutable classes in Foundation is kept somewhere and prevents the object from being released.

An interesting part is using the mutable classes on Foundation like NSMutableString, NSMutableData, etc.

They work exactly as expected😵‍💫😵‍💫😵‍💫😵‍💫😵‍💫😵‍💫😵‍💫😵‍💫😵‍💫😵‍💫😵‍💫😵‍💫😵‍💫😵‍💫😵‍💫😵‍💫😵‍💫😵‍💫!!

Try the codes below, and you will see that NSHashTable will be empty after we remove the reference to str.

Pitfall Number 4

Again there is some issue with Foundation classes under the following conditions:

  1. Using Swift
  2. Using Mutable Classes Of Foundation(NSMutableString ….)
  3. Set The NSHashTable’s option to copy.

Notice, This issue will not raise if you use Objective-C language while the classes are available on objc.

The reason is that when you are using copy option, NSHashTable somehow creates a copy with different types which were not ported into swift.

The code above will result in a crash, and the error will be:

Fatal error: NSArray element failed to match the Swift Array Element type, Expected NSMutableData but found _NSZeroData

It seems that the _NSZeroData and several more classes are some interfaces that are internal classes of Foundation and can not be used on swift. But if you try it with Objective-C, it won’t raise any issue.

I hope Apple will fix these issues or clarify why this is happening. Until then, the more swift way can be found on this link thanks to ihor😎:

I hope you find this helpful article, Have a lovely weekend🥳

References

--

--

Kiarash Vosough
Divar Mobile Engineering

iOS Developer and Swift Lover with over 5 years of experience on Apple platforms. Find me on Any Social Media by searching Kiarash Vosough:)