本文源自本人的学习记录整理与理解,其中参考阅读了部分优秀的博客和书籍,尽量以通俗简单的语句转述。引用到的地方如有遗漏或未能一一列举原文出处还望见谅与指出,另文章内容如有不妥之处还望指教,万分感谢。
这篇文章源自一次关于@[].mutableCopy
和[NSMutableArray array]
区别的讨论
- MRC环境下的区别:
像这种 [NSMutableArray array] 类方法,array 啥的,初始化的时候会调用 autorelease,会找合适的时机进行释放,可以理解为:
-
[[[NSMutableArray alloc] init] autorelease]
半自动管理内存 -
@[].mutableCopy
不会调用 autorelease ,需要手动管理内存。
结论:推荐使用后者,性能更好
直接来看两者在运行时执行的源码:
@[].mutableCopy:
(void *)objc_msgSend(
(NSArray *(*)(Class, SEL, ObjectType _Nonnull const * _Nonnull, NSUInteger))(void *)objc_msgSend
(objc_getClass("NSArray"),
sel_registerName("arrayWithObjects:count:"),
(const id *)__NSContainer_literal(0U).arr, 0U),
sel_registerName("mutableCopy"));
简化后代码
(void *)objc_msgSend(
//1. 找到NSArray 给他发送 arrayWithObjects:count:消息 ,生成一个NSArray的实例对象
objc_msgSend(
objc_getClass("NSArray"),
sel_registerName("arrayWithObjects:count:"),
__NSContainer_literal(0U).arr, 0U
),
//2. 对创建出来的对象发送 mutableCopy 消息,返回一个可变数组
sel_registerName("mutableCopy")
);
[NSMutableArray array]
(void *)objc_msgSend(
objc_getClass("NSMutableArray"),
sel_registerName("array")
);
sel_registerName的内部实现
sel_registerName 内部调用__sel_registerName
- selLock锁是否可用
- 判断方法明是否存在,不存在返回0
- 搜索这个方法是否创建过,如果是就直接返回,这也变相说明 @selector(selector) 同名方法的函数地址相同
- 没有就创建(sel_alloc)并返回,过程中用到了条件锁conditional_mutex
- namedSelectors :方法集合,内部存储了一个个哈希表
static SEL __sel_registerName(const char *name, bool shouldLock, bool copy) {
SEL result = 0;
if (shouldLock) {
selLock.assertUnlocked();
}else {
selLock.assertLocked()
};
if (!name) return (SEL)0;
result = search_builtins(name);
if (result) return result;
conditional_mutex_locker_t lock(selLock, shouldLock);
//从这行代码可以看出 namedSelectors是一个集合,存储的元素是哈希 表;通过insert插入
auto it = namedSelectors.get().insert(name);
if (it.second) {
// No match. Insert.
*it.first = (const char *)sel_alloc(name, copy);
}
return (SEL)*it.first;
}
扩展:
static objc::ExplicitInitDenseSet<const char *> namedSelectors;
class ExplicitInitDenseSet : public ExplicitInit<DenseSet<Value>> { };
父类是 ExplicitInit ,ExplicitInit 的成员是 DenseSet 类型;
DenseSet
/// Implement a set of hash tables based on dense probe.
/// 实现基于密集探测的哈希表集。
template <typename ValueT, typename ValueInfoT = DenseMapInfo<ValueT>>
class DenseSet : public detail::DenseSetImpl<
ValueT,
DenseMap<ValueT, detail::DenseSetEmpty,
DenseMapValueInfo<detail::DenseSetEmpty>,
ValueInfoT,
detail::DenseSetPair<ValueT>
>,
ValueInfoT>
{
using BaseT =
detail::DenseSetImpl<
ValueT,
DenseMap<ValueT,
detail::DenseSetEmpty,
DenseMapValueInfo<detail::DenseSetEmpty>,
ValueInfoT,
detail::DenseSetPair<ValueT>
>,
ValueInfoT
>;
public:
using BaseT::BaseT;
};
- 关于
DenseSetImpl
:
class DenseSetImpl {
static_assert(sizeof(typename MapTy::value_type) == sizeof(ValueT),
"DenseMap buckets unexpectedly large!");
MapTy TheMap;
template <typename T>
using const_arg_type_t = typename const_pointer_or_const_ref<T>::type;
public:
using key_type = ValueT;
using value_type = ValueT;
using size_type = unsigned;
explicit DenseSetImpl(unsigned InitialReserve = 0) : TheMap(InitialReserve) {}
DenseSetImpl(std::initializer_list<ValueT> Elems)
: DenseSetImpl(PowerOf2Ceil(Elems.size())) {
insert(Elems.begin(), Elems.end());
}
bool empty() const { return TheMap.empty(); }
size_type size() const { return TheMap.size(); }
size_t getMemorySize() const { return TheMap.getMemorySize(); }
/// Grow the DenseSet so that it has at least Size buckets. Will not shrink
/// the Size of the set.
void resize(size_t Size) { TheMap.resize(Size); }
/// Grow the DenseSet so that it can contain at least \p NumEntries items
/// before resizing again.
void reserve(size_t Size) { TheMap.reserve(Size); }
void clear() {
TheMap.clear();
}
/// Return 1 if the specified key is in the set, 0 otherwise.
size_type count(const_arg_type_t<ValueT> V) const {
return TheMap.count(V);
}
bool erase(const ValueT &V) {
return TheMap.erase(V);
}
void swap(DenseSetImpl &RHS) { TheMap.swap(RHS.TheMap); }
// Iterators.
class ConstIterator;
class Iterator {
typename MapTy::iterator I;
friend class DenseSetImpl;
friend class ConstIterator;
public:
using difference_type = typename MapTy::iterator::difference_type;
using value_type = ValueT;
using pointer = value_type *;
using reference = value_type &;
using iterator_category = std::forward_iterator_tag;
Iterator() = default;
Iterator(const typename MapTy::iterator &i) : I(i) {}
ValueT &operator*() { return I->getFirst(); }
const ValueT &operator*() const { return I->getFirst(); }
ValueT *operator->() { return &I->getFirst(); }
const ValueT *operator->() const { return &I->getFirst(); }
Iterator& operator++() { ++I; return *this; }
Iterator operator++(int) { auto T = *this; ++I; return T; }
bool operator==(const ConstIterator& X) const { return I == X.I; }
bool operator!=(const ConstIterator& X) const { return I != X.I; }
};
class ConstIterator {
typename MapTy::const_iterator I;
friend class DenseSet;
friend class Iterator;
public:
using difference_type = typename MapTy::const_iterator::difference_type;
using value_type = ValueT;
using pointer = const value_type *;
using reference = const value_type &;
using iterator_category = std::forward_iterator_tag;
ConstIterator() = default;
ConstIterator(const Iterator &B) : I(B.I) {}
ConstIterator(const typename MapTy::const_iterator &i) : I(i) {}
const ValueT &operator*() const { return I->getFirst(); }
const ValueT *operator->() const { return &I->getFirst(); }
ConstIterator& operator++() { ++I; return *this; }
ConstIterator operator++(int) { auto T = *this; ++I; return T; }
bool operator==(const ConstIterator& X) const { return I == X.I; }
bool operator!=(const ConstIterator& X) const { return I != X.I; }
};
using iterator = Iterator;
using const_iterator = ConstIterator;
iterator begin() { return Iterator(TheMap.begin()); }
iterator end() { return Iterator(TheMap.end()); }
const_iterator begin() const { return ConstIterator(TheMap.begin()); }
const_iterator end() const { return ConstIterator(TheMap.end()); }
iterator find(const_arg_type_t<ValueT> V) { return Iterator(TheMap.find(V)); }
const_iterator find(const_arg_type_t<ValueT> V) const {
return ConstIterator(TheMap.find(V));
}
/// Alternative version of find() which allows a different, and possibly less
/// expensive, key type.
/// The DenseMapInfo is responsible for supplying methods
/// getHashValue(LookupKeyT) and isEqual(LookupKeyT, KeyT) for each key type
/// used.
template <class LookupKeyT>
iterator find_as(const LookupKeyT &Val) {
return Iterator(TheMap.find_as(Val));
}
template <class LookupKeyT>
const_iterator find_as(const LookupKeyT &Val) const {
return ConstIterator(TheMap.find_as(Val));
}
void erase(Iterator I) { return TheMap.erase(I.I); }
void erase(ConstIterator CI) { return TheMap.erase(CI.I); }
std::pair<iterator, bool> insert(const ValueT &V) {
detail::DenseSetEmpty Empty;
return TheMap.try_emplace(V, Empty);
}
std::pair<iterator, bool> insert(ValueT &&V) {
detail::DenseSetEmpty Empty;
return TheMap.try_emplace(std::move(V), Empty);
}
/// Alternative version of insert that uses a different (and possibly less
/// expensive) key type.
template <typename LookupKeyT>
std::pair<iterator, bool> insert_as(const ValueT &V,
const LookupKeyT &LookupKey) {
return TheMap.insert_as({V, detail::DenseSetEmpty()}, LookupKey);
}
template <typename LookupKeyT>
std::pair<iterator, bool> insert_as(ValueT &&V, const LookupKeyT &LookupKey) {
return TheMap.insert_as({std::move(V), detail::DenseSetEmpty()}, LookupKey);
}
// Range insertion of values.
template<typename InputIt>
void insert(InputIt I, InputIt E) {
for (; I != E; ++I)
insert(*I);
}
};