在方法调配中使用 dispatch_once
Using dispatch_once in method swizzling
在 NSHipter 关于 method swizzling 的文章中,它说 "Swizzling should always be done in a dispatch_once." 为什么这是必要的,因为 +load 每个 class 只发生一次?
我认为那篇文章暗示 "Swizzling should be done in +load"。但是您仍然可以在其他地方进行混合。在这种情况下,您应该在 dispatch_once 中进行 swizzling 以实现原子性。我认为在 +load 中也没有必要将 swizzling 包装在 dispatch_once 中。
不需要。 +load
保证线程安全和可重入。参见 objc-runtime-new.mm
中的 load_images
:
/***********************************************************************
* load_images
* Process +load in the given images which are being mapped in by dyld.
* Calls ABI-agnostic code after taking ABI-specific locks.
*
* Locking: write-locks runtimeLock and loadMethodLock
**********************************************************************/
const char *
load_images(enum dyld_image_states state, uint32_t infoCount,
const struct dyld_image_info infoList[])
{
BOOL found;
recursive_mutex_lock(&loadMethodLock);
// Discover load methods
rwlock_write(&runtimeLock);
found = load_images_nolock(state, infoCount, infoList);
rwlock_unlock_write(&runtimeLock);
// Call +load methods (without runtimeLock - re-entrant)
if (found) {
call_load_methods();
}
recursive_mutex_unlock(&loadMethodLock);
return nil;
}
注意递归互斥锁,它保证所有加载都在阻塞时完成,并且 call_load_methods()
将确保 +load 在每个 +load
.
的实现中只被调用一次
请注意 +load
的特殊之处在于它可以在每个 class 的基础上有多个实现,这也是为什么它更适合 swizzling 的原因之一 - 你的 +load
,以及原来的+load
是保证被调用的
奖励:call_load_methods()
上的相关文档直接说明了为什么这是线程安全的:
/***********************************************************************
* call_load_methods
* Call all pending class and category +load methods.
* Class +load methods are called superclass-first.
* Category +load methods are not called until after the parent class's +load.
*
* This method must be RE-ENTRANT, because a +load could trigger
* more image mapping. In addition, the superclass-first ordering
* must be preserved in the face of re-entrant calls. Therefore,
* only the OUTERMOST call of this function will do anything, and
* that call will handle all loadable classes, even those generated
* while it was running.
*
* The sequence below preserves +load ordering in the face of
* image loading during a +load, and make sure that no
* +load method is forgotten because it was added during
* a +load call.
* Sequence:
* 1. Repeatedly call class +loads until there aren't any more
* 2. Call category +loads ONCE.
* 3. Run more +loads if:
* (a) there are more classes to load, OR
* (b) there are some potential category +loads that have
* still never been attempted.
* Category +loads are only run once to ensure "parent class first"
* ordering, even if a category +load triggers a new loadable class
* and a new loadable category attached to that class.
*
* Locking: loadMethodLock must be held by the caller
* All other locks must not be held.
**********************************************************************/
void call_load_methods(void)
在 NSHipter 关于 method swizzling 的文章中,它说 "Swizzling should always be done in a dispatch_once." 为什么这是必要的,因为 +load 每个 class 只发生一次?
我认为那篇文章暗示 "Swizzling should be done in +load"。但是您仍然可以在其他地方进行混合。在这种情况下,您应该在 dispatch_once 中进行 swizzling 以实现原子性。我认为在 +load 中也没有必要将 swizzling 包装在 dispatch_once 中。
不需要。 +load
保证线程安全和可重入。参见 objc-runtime-new.mm
中的 load_images
:
/***********************************************************************
* load_images
* Process +load in the given images which are being mapped in by dyld.
* Calls ABI-agnostic code after taking ABI-specific locks.
*
* Locking: write-locks runtimeLock and loadMethodLock
**********************************************************************/
const char *
load_images(enum dyld_image_states state, uint32_t infoCount,
const struct dyld_image_info infoList[])
{
BOOL found;
recursive_mutex_lock(&loadMethodLock);
// Discover load methods
rwlock_write(&runtimeLock);
found = load_images_nolock(state, infoCount, infoList);
rwlock_unlock_write(&runtimeLock);
// Call +load methods (without runtimeLock - re-entrant)
if (found) {
call_load_methods();
}
recursive_mutex_unlock(&loadMethodLock);
return nil;
}
注意递归互斥锁,它保证所有加载都在阻塞时完成,并且 call_load_methods()
将确保 +load 在每个 +load
.
请注意 +load
的特殊之处在于它可以在每个 class 的基础上有多个实现,这也是为什么它更适合 swizzling 的原因之一 - 你的 +load
,以及原来的+load
是保证被调用的
奖励:call_load_methods()
上的相关文档直接说明了为什么这是线程安全的:
/***********************************************************************
* call_load_methods
* Call all pending class and category +load methods.
* Class +load methods are called superclass-first.
* Category +load methods are not called until after the parent class's +load.
*
* This method must be RE-ENTRANT, because a +load could trigger
* more image mapping. In addition, the superclass-first ordering
* must be preserved in the face of re-entrant calls. Therefore,
* only the OUTERMOST call of this function will do anything, and
* that call will handle all loadable classes, even those generated
* while it was running.
*
* The sequence below preserves +load ordering in the face of
* image loading during a +load, and make sure that no
* +load method is forgotten because it was added during
* a +load call.
* Sequence:
* 1. Repeatedly call class +loads until there aren't any more
* 2. Call category +loads ONCE.
* 3. Run more +loads if:
* (a) there are more classes to load, OR
* (b) there are some potential category +loads that have
* still never been attempted.
* Category +loads are only run once to ensure "parent class first"
* ordering, even if a category +load triggers a new loadable class
* and a new loadable category attached to that class.
*
* Locking: loadMethodLock must be held by the caller
* All other locks must not be held.
**********************************************************************/
void call_load_methods(void)