swifter-tips / Public-Issues

Public issues for Swifter-tips book
103 stars 3 forks source link

建议在《哈希》这一节提一下NSString/String的坑 #46

Closed owenzhao closed 9 years ago

owenzhao commented 9 years ago

它们仅能比较当前是否相等,不能存起来之后再比较。因为即便相等的,每次程序运行时的hash/hashvalue也可能不同。

onevcat commented 9 years ago

哈?存起来是什么意思?能举一些详细的例子么?

owenzhao commented 9 years ago

就是因为hash这个协议要求的是equal的两个string,hash值必须相同。这个从逻辑上讲,其实是单向的,即它包括两方面: 1 equal的两个string,hash值相同 2 hash值相同的两个string,不一定equal。

第二点虽然不自然,但是逻辑上没有错。而苹果的string的hash,恰恰符合这两点。因此,不equal的两个string,也会存在hash值相等的情况。hash值相等的两个string也不一定相等,这就是string的hash的坑。因为实际上,string的hash算法只是记录了string的前多少个的hash值,而不是只是整个的string的hash值。因此,如果你需要双向的string的hash,就需要自己设计一个hash,比如用md5或者sha之类的。而不能依赖string默认的hash实现。

另外,string还有不同程序之间的hash不等,系统升级之后可能会不等等其他的问题。简而言之,string的hash,只是符合1和2这两条,其它的都没有承诺。

苹果对于string的hash是这么写的:

If two string objects are equal (as determined by the isEqualToString: method), they must have the same hash value. This property fulfills this requirement.

You should not rely on this property having the same hash value across releases of OS X.

给一个文章:http://denniswilson.de/blog/nsstring-hash-is-bad/

owenzhao commented 9 years ago

举一个例子。比如我做的一个app有需要向realm存入文件的bookmark(NSData类型)作为主键。由于realm不支持NSData作为主键,于是我就想可以用description,然后再用hash就可以了。结果发现做出来的bookmark数量,比实际的文件数量少了不少。经过仔细查找才发现。虽然bookmark都不相同。但是他们的hash存在相同的情况,因此前面的就被后面的覆盖了。

另外,这些hash不能保证前后的一致性,因此不适合保存作为以后进行查询。所以如果有需要一致的hash,都需要自己单独实现。

onevcat commented 9 years ago

hash值相同的两个string,不一定equal。第二点虽然不自然,但是逻辑上没有错。...hash值相等的两个string也不一定相等,这就是string的hash的坑

Hash 本身定义不就是这样的么?这个不算是什么坑吧...

于是我就想可以用description,然后再用hash就可以了

为什么会用 hash 后的值作键....在一个 hash 散列里,你用 hash 值做键的意思就是 hash 值还会被 hash 一次...

书里的《哈希》这节并没有想要引入这些错误用法的示例,关于 hash 的这些“坑”,其实就是一句话:

我们需要为判等结果为相同的对象提供相同的哈希值

据此来实现的 -hash 或者 Hashable,其最终的目的就是为了给哈希散列提供可靠的性能保障,而不是让你自己来使用 hash 值做什么事情 (除非你在实现一个你自己的散列数据结构)。

可以考虑在这节里添加关于不同系统下 hash 值会不相同的一些说明,其他部分的话因为可以说都是误用,额外说明的意义并不大。

谢谢反馈。

onevcat commented 9 years ago

2.0.2 中添加了相关说明