簡(jiǎn)介
鍵值觀察是模型 - 視圖 - 控制器應(yīng)用程序(MVC)中各層之間通信的一種特別有用的技術(shù)嫩与。
KVOController以Cocoa經(jīng)過時(shí)間考驗(yàn)的鍵值觀察實(shí)現(xiàn)為基礎(chǔ)。 它提供了一個(gè)簡(jiǎn)單进倍、現(xiàn)代的API申眼,同時(shí)也是線程安全的。
如果你在項(xiàng)目中有使用 KVO 碘裕,那么 KVOController 絕對(duì)是個(gè)好選擇凑耻。它是 facebook 開源的一個(gè) KVO 增強(qiáng)框架太示。
有以下幾個(gè)特性:
使用 Blocks 、自定義 Actions 或者 NSKeyValueObserving 回調(diào)進(jìn)行通知
觀測(cè)者移除時(shí)無異常
控制器 dealloc 時(shí)隱式的觀測(cè)者移除
提升使用 NSKeyValueObservingInitial 的性能
線程安全并提供在觀測(cè)者恢復(fù)時(shí)額外的保護(hù)
開發(fā)環(huán)境
KVOController利用了當(dāng)前Objective-C中runtime的優(yōu)勢(shì)香浩,以及ARC和指針集合類类缤。
開發(fā)環(huán)境要求:
- iOS 6 or later.
- OS X 10.7 or later.
集成開發(fā)環(huán)境(IDE,Integrated Development Environment ):
- Xcode 8.0+
安裝
要使用CocoaPods進(jìn)行安裝邻吭,請(qǐng)將以下內(nèi)容添加到項(xiàng)目Podfile中:
pod 'KVOController'
要使用Carthage進(jìn)行安裝餐弱,請(qǐng)將以下內(nèi)容添加到項(xiàng)目Cartfile中:
github "facebook/KVOController"
或者,將FBKVOController.h
和FBKVOController.m
拖放到Xcode項(xiàng)目中囱晴, 并同意在需要時(shí)復(fù)制文件岸裙。 對(duì)于iOS應(yīng)用程序,您可以選擇鏈接KVOController項(xiàng)目的靜態(tài)庫(kù)目標(biāo)速缆。
使用CocoaPods或Carthage安裝后,在Objective-C中添加以下內(nèi)容進(jìn)行導(dǎo)入:
#import <KVOController/KVOController.h>
使用
// Objective-C
#import <KVOController/KVOController.h>
// create KVO controller with observer
FBKVOController *KVOController = [FBKVOController controllerWithObserver:self];
self.KVOController = KVOController;
// observe clock date property
[self.KVOController observe:clock keyPath:@"date" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew block:^(ClockView *clockView, Clock *clock, NSDictionary *change) {
// update clock view with new value
clockView.date = change[NSKeyValueChangeNewKey];
}];
KVOController在Swift中運(yùn)行良好恩闻,但有兩點(diǎn)要求:
- 觀察者應(yīng)該是
NSObject
的子類 - 觀察屬性必須標(biāo)記為
dynamic
// Swift
class TasksListViewModel: NSObject {
dynamic var tasksList: [TaskList] = []
}
/// In ViewController.swift
import KVOController
kvoController.observe(viewModel,
keyPath: "listsDidChange",
options: [.new, .initial]) { (viewController, viewModel, change) in
self.taskListsTableView.reloadData()
}
測(cè)試
單元測(cè)試包括使用CocoaPods來管理依賴項(xiàng)。 如果您尚未安裝CocoaPods,請(qǐng)安裝它們领猾。 然后,在命令行中翅楼,導(dǎo)航到根KVOController目錄并鍵入:
pod install
這將在OCHamcrest
和OCMockito
上安裝和添加測(cè)試依賴項(xiàng)。 重新打開Xcode的KVOController工作區(qū)和Test真慢,?U毅臊。
開源許可證
KVOController根據(jù) BSD許可證
發(fā)布。 有關(guān)詳細(xì)信息黑界,請(qǐng)參閱LICENSE文件管嬉。
BSD License
For KVOController software
Copyright (c) 2014, Facebook, Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name Facebook nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
源碼分析
KVOController是對(duì)Cocoa中KVO進(jìn)行了封裝,整個(gè)框架只有兩個(gè)實(shí)現(xiàn)文件:
#import <KVOController/FBKVOController.h>
#import <KVOController/NSObject+FBKVOController.h>
- 類圖
上面類圖的PlantUML源碼如下:
@startuml
skinparam class {
BackgroundColor Beige
ArrowColor Indigo
BorderColor Indigo
BackgroundColor<<Foo>> Wheat
BorderColor<<Foo>> Tomato
}
skinparam stereotypeCBackgroundColor YellowGreen
skinparam stereotypeCBackgroundColor<< Foo >> DimGray
NSObject <|-- _FBKVOInfo
NSObject <|-- _FBKVOSharedController
NSObject <|-- FBKVOController
FBKVOController <--o _FBKVOSharedController
FBKVOController <--o _FBKVOInfo
note top of NSObject
在Objective-C中,幾乎所有的類都是繼承與NSObject,剩下不繼承NSObject的都繼承NSProxy.
NSObject(NSKeyValueObserving) 接收KVO所監(jiān)聽的屬性值發(fā)生變化的通知
end note
class NSObject {
# hash : NSUInteger
# superclass : Class
# description : NSString
# debugDescription : NSString
-init()
-hash()
-isEqual:(id)object()
-debugDescription()
-dealloc()
-observeValueForKeyPath: ofObject: change: context:()
}
class _FBKVOInfo {
+_controller : FBKVOController
+_keyPath : NSString
+_options : NSKeyValueObservingOptions
+_action : SEL
+_context : void *
+_block : FBKVONotificationBlock
+_state : _FBKVOInfoState
-initWithController: keyPath: options: block: action: context:()
-hash()
-isEqual:(id)object()
-debugDescription()
}
class _FBKVOSharedController {
#_infos : NSHashTable<_FBKVOInfo *>
#_mutex : pthread_mutex_t
+sharedController()
-observe: info:()
-unobserve: info:()
-unobserve: infos:()
-init()
-dealloc()
-debugDescription()
-observeValueForKeyPath: ofObject: change: context:()
}
class FBKVOController {
#observer : id
-_objectInfosMap : NSMapTable<id, NSMutableSet<_FBKVOInfo *> *>
-_lock : pthread_mutex_t
+controllerWithObserver:()
-initWithObserver: retainObserved:()
-initWithObserver:()
-init()
-new()
-observe: keyPath: options: block:()
-observe: keyPath: options: action:()
-observe: keyPath: options: context:()
-observe: keyPaths: options: block:()
-observe: keyPaths: options: action:()
-observe: keyPaths: options: context:()
-unobserve: keyPath:()
-unobserve:()
-unobserveAll()
}
@enduml
-
核心調(diào)用棧
Add Observer
KVOController實(shí)現(xiàn)流程:
Observer會(huì)創(chuàng)建一個(gè)FBKVOController的屬性朗鸠;
FBKVOController中包含一個(gè)NSMapTable的成員屬性蚯撩,用來存儲(chǔ)observer的KVO信息;
FBKVOController創(chuàng)建一個(gè)_FBKVOInfo類型的實(shí)例烛占,實(shí)例中存儲(chǔ)了和KVO操作相關(guān)的信息(keypath等)胎挎,然后將需要觀察的對(duì)象Target作為Key,_FBKVOInfo的實(shí)例加入數(shù)組(對(duì)同一個(gè)Target的不同keypath的多次KVO操作)并把數(shù)組作為Value忆家,存入步驟2中的mapTable中犹菇;
FBKVOController會(huì)調(diào)用_FBKVOSharedController的單例中的方法,同時(shí)將步驟3創(chuàng)建的info和觀察的target傳入給這個(gè)方法芽卿,這個(gè)單例進(jìn)行了最終的KVO操作揭芍;
-
_FBKVOSharedController的單例調(diào)用系統(tǒng)KVO方法,將自己作為觀察者來觀察Target對(duì)象蹬竖。
Remove Observer
在Observer內(nèi)存被釋放沼沈,執(zhí)行dealloc時(shí),其創(chuàng)建的FBKVOController屬性的析構(gòu)方法dealloc會(huì)通過KVOInfoMap找到所有KVO的對(duì)象币厕,并執(zhí)行移除觀察的操作列另,十分巧妙的設(shè)計(jì)!
但是在使用的過程中還是有一些注意事項(xiàng)的:
首先旦装,F(xiàn)BKVOController使用block來傳遞系統(tǒng)KVO的回調(diào)页衙,因此要注意retain cycle。
其次阴绢,在使用的過程中店乐,target不能強(qiáng)引用observer,否則也會(huì)形成retain cycle呻袭。
- 核心代碼分析
- FBKVOController構(gòu)造函數(shù):
- (instancetype)initWithObserver:(nullable id)observer retainObserved:(BOOL)retainObserved
{
self = [super init];
if (nil != self) {
_observer = observer;
NSPointerFunctionsOptions keyOptions = retainObserved ? NSPointerFunctionsStrongMemory|NSPointerFunctionsObjectPointerPersonality : NSPointerFunctionsWeakMemory|NSPointerFunctionsObjectPointerPersonality;
_objectInfosMap = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:NSPointerFunctionsStrongMemory|NSPointerFunctionsObjectPersonality capacity:0];
pthread_mutex_init(&_lock, NULL);
}
return self;
}
NSMapTable
是早在 Mac OS X 10.5(Leopard)就引入的集合類型眨八。
NSMapTable
除了使用NSPointerFunctionsCopyIn
,任何的默認(rèn)行為都會(huì)retain(或弱引用)鍵對(duì)象而不會(huì)拷貝它左电,與CFDictionary
的行為相同而與NSDictionary
不同廉侧。當(dāng)你需要一個(gè)字典页响,它的鍵沒有實(shí)現(xiàn)NSCopying
協(xié)議,比如UIView
段誊,的時(shí)候非常有用闰蚕。
如果你好奇為什么蘋果”忘記”為NSMapTable
增加下標(biāo),你現(xiàn)在知道了连舍。下標(biāo)訪問需要一個(gè)id<NSCopying>
作為key没陡,對(duì)NSMapTable
來說這不是強(qiáng)制的。如果不通過一個(gè)非法的API協(xié)議或者移除NSCopying
協(xié)議來削弱全局下標(biāo)索赏,是沒有辦法給它增加下標(biāo)的盼玄。
NSMapTable
查詢性能只比NSDictionary
略微慢一點(diǎn)。如果你需要一個(gè)不retain鍵的字典参滴,放棄CFDictionary
使用它吧强岸。
pthread_mutex
為C語言定義下多線程加鎖方式。
1:pthread_mutex_init(pthread_mutex_t mutex,const pthread_mutexattr_t attr);初始化鎖變量mutex砾赔。attr為鎖屬性蝌箍,NULL值為默認(rèn)屬性。
2:pthread_mutex_lock(pthread_mutex_t mutex);加鎖
3:pthread_mutex_tylock(*pthread_mutex_t *mutex);加鎖暴心,但是與2不一樣的是當(dāng)鎖已經(jīng)在使用的時(shí)候妓盲,返回為EBUSY,而不是掛起等待专普。
4:pthread_mutex_unlock(pthread_mutex_t *mutex);釋放鎖
5:pthread_mutex_destroy(pthread_mutex_t* mutex);使用完后釋放
- 單例_FBKVOSharedController監(jiān)聽object的info.keyPath屬性
- (void)observe:(id)object info:(nullable _FBKVOInfo *)info
{
if (nil == info) {
return;
}
// register info
pthread_mutex_lock(&_mutex);
[_infos addObject:info];
pthread_mutex_unlock(&_mutex);
// add observer
[object addObserver:self forKeyPath:info->_keyPath options:info->_options context:(void *)info];
if (info->_state == _FBKVOInfoStateInitial) {
info->_state = _FBKVOInfoStateObserving;
} else if (info->_state == _FBKVOInfoStateNotObserving) {
// this could happen when `NSKeyValueObservingOptionInitial` is one of the NSKeyValueObservingOptions,
// and the observer is unregistered within the callback block.
// at this time the object has been registered as an observer (in Foundation KVO),
// so we can safely unobserve it.
[object removeObserver:self forKeyPath:info->_keyPath context:(void *)info];
}
}
- 單例_FBKVOSharedController接收KVO所監(jiān)聽的屬性值發(fā)生變化的通知
- (void)observeValueForKeyPath:(nullable NSString *)keyPath
ofObject:(nullable id)object
change:(nullable NSDictionary<NSString *, id> *)change
context:(nullable void *)context
{
NSAssert(context, @"missing context keyPath:%@ object:%@ change:%@", keyPath, object, change);
_FBKVOInfo *info;
{
// lookup context in registered infos, taking out a strong reference only if it exists
pthread_mutex_lock(&_mutex);
info = [_infos member:(__bridge id)context];
pthread_mutex_unlock(&_mutex);
}
if (nil != info) {
// take strong reference to controller
FBKVOController *controller = info->_controller;
if (nil != controller) {
// take strong reference to observer
id observer = controller.observer;
if (nil != observer) {
// dispatch custom block or action, fall back to default action
if (info->_block) {
NSDictionary<NSString *, id> *changeWithKeyPath = change;
// add the keyPath to the change dictionary for clarity when mulitple keyPaths are being observed
if (keyPath) {
NSMutableDictionary<NSString *, id> *mChange = [NSMutableDictionary dictionaryWithObject:keyPath forKey:FBKVONotificationKeyPathKey];
[mChange addEntriesFromDictionary:change];
changeWithKeyPath = [mChange copy];
}
info->_block(observer, object, changeWithKeyPath);
} else if (info->_action) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[observer performSelector:info->_action withObject:change withObject:object];
#pragma clang diagnostic pop
} else {
[observer observeValueForKeyPath:keyPath ofObject:object change:change context:info->_context];
}
}
}
}
}
addEntriesFromDictionary
拼接字典悯衬,需要注意的是:
在相同key的情況下,相對(duì)應(yīng)的value會(huì)被賦予新值檀夹,使用時(shí)注意順序
NSDictionary *dic1 = [NSDictionary dictionaryWithObjectsAndKeys:@"BMW",@"CarLogo",@"Red",@"CarColor",@"MountainX",@"name",nil];
NSMutableDictionary *dic2 = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"JSK",@"name",@"25", @"age", nil];
[dic2 addEntriesFromDictionary:dic1];
NSLog(@"%@",dic2);
// 控制臺(tái)打印如下:
{
CarColor = Red;
CarLogo = BMW;
age = 25;
name = MountainX;
}
- 分類 NSObject+FBKVOController
利用分類和Runtime中的關(guān)聯(lián)機(jī)制為NSObject添加了兩個(gè)懶加載的屬性:
/**
Copyright (c) 2014-present, Facebook, Inc.
All rights reserved.
This source code is licensed under the BSD-style license found in the
LICENSE file in the root directory of this source tree. An additional grant
of patent rights can be found in the PATENTS file in the same directory.
*/
#import <Foundation/Foundation.h>
#import "FBKVOController.h"
NS_ASSUME_NONNULL_BEGIN
/**
Category that adds built-in `KVOController` and `KVOControllerNonRetaining` on any instance of `NSObject`.
This makes it convenient to simply create and forget a `FBKVOController`,
and when this object gets dealloc'd, so will the associated controller and the observation info.
*/
@interface NSObject (FBKVOController)
/**
@abstract Lazy-loaded FBKVOController for use with any object
@return FBKVOController associated with this object, creating one if necessary
@discussion This makes it convenient to simply create and forget a FBKVOController, and when this object gets dealloc'd, so will the associated controller and the observation info.
*/
@property (nonatomic, strong) FBKVOController *KVOController;
/**
@abstract Lazy-loaded FBKVOController for use with any object
@return FBKVOController associated with this object, creating one if necessary
@discussion This makes it convenient to simply create and forget a FBKVOController.
Use this version when a strong reference between controller and observed object would create a retain cycle.
When not retaining observed objects, special care must be taken to remove observation info prior to deallocation of the observed object.
*/
@property (nonatomic, strong) FBKVOController *KVOControllerNonRetaining;
@end
NS_ASSUME_NONNULL_END
顧名思義筋粗, KVOControllerNonRetaining
在使用時(shí)并不會(huì)持有被觀察的對(duì)象
實(shí)現(xiàn)方式:
/**
Copyright (c) 2014-present, Facebook, Inc.
All rights reserved.
This source code is licensed under the BSD-style license found in the
LICENSE file in the root directory of this source tree. An additional grant
of patent rights can be found in the PATENTS file in the same directory.
*/
#import "NSObject+FBKVOController.h"
#import <objc/message.h>
#if !__has_feature(objc_arc)
#error This file must be compiled with ARC. Convert your project to ARC or specify the -fobjc-arc flag.
#endif
#pragma mark NSObject Category -
NS_ASSUME_NONNULL_BEGIN
static void *NSObjectKVOControllerKey = &NSObjectKVOControllerKey;
static void *NSObjectKVOControllerNonRetainingKey = &NSObjectKVOControllerNonRetainingKey;
@implementation NSObject (FBKVOController)
- (FBKVOController *)KVOController
{
id controller = objc_getAssociatedObject(self, NSObjectKVOControllerKey);
// lazily create the KVOController
if (nil == controller) {
controller = [FBKVOController controllerWithObserver:self];
self.KVOController = controller;
}
return controller;
}
- (void)setKVOController:(FBKVOController *)KVOController
{
objc_setAssociatedObject(self, NSObjectKVOControllerKey, KVOController, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (FBKVOController *)KVOControllerNonRetaining
{
id controller = objc_getAssociatedObject(self, NSObjectKVOControllerNonRetainingKey);
if (nil == controller) {
controller = [[FBKVOController alloc] initWithObserver:self retainObserved:NO];
self.KVOControllerNonRetaining = controller;
}
return controller;
}
- (void)setKVOControllerNonRetaining:(FBKVOController *)KVOControllerNonRetaining
{
objc_setAssociatedObject(self, NSObjectKVOControllerNonRetainingKey, KVOControllerNonRetaining, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
NS_ASSUME_NONNULL_END
其中
static void *NSObjectKVOControllerKey = &NSObjectKVOControllerKey;
static void *NSObjectKVOControllerNonRetainingKey = &NSObjectKVOControllerNonRetainingKey;
這種聲明方式在編譯的時(shí)候創(chuàng)建一個(gè)唯一的指針a method to create a unique pointer at compile time
.
well, so idea for these constants is to have some unique value, that will not repeat anywhere in the program, but we don't really care about its content.
now, instead of coming up with some random string/number etc, we just create a pointer, and put its address as content, this way it's unique and the code is simple is nice :)
這種聲明方式也常用于kvo,用來當(dāng)做context的key來添加.
因?yàn)閗vo的時(shí)候context如果不小心重復(fù)了,會(huì)發(fā)生奇怪的事情.用這種方式可以避免.
static void *CapturingStillImageContext = &CapturingStillImageContext;
[self addObserver:self forKeyPath:@"stillImageOutput.capturingStillImage" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:CapturingStillImageContext];
PS
- 關(guān)聯(lián)
關(guān)聯(lián)是指把兩個(gè)對(duì)象相互關(guān)聯(lián)起來,使得其中的一個(gè)對(duì)象作為另外一個(gè)對(duì)象的一部分炸渡。
關(guān)聯(lián)特性只有在Mac OS X V10.6以及以后的版本上才是可用的娜亿。
使用關(guān)聯(lián),我們可以不用修改類的定義而為其對(duì)象增加存儲(chǔ)空間蚌堵。這在我們無法訪問到類的源碼的時(shí)候或者是考慮到二進(jìn)制兼容性的時(shí)候是非常有用买决。
關(guān)聯(lián)是基于關(guān)鍵字的,因此吼畏,我們可以為任何對(duì)象增加任意多的關(guān)聯(lián)督赤,每個(gè)都使用不同的關(guān)鍵字即可。關(guān)聯(lián)是可以保證被關(guān)聯(lián)的對(duì)象在關(guān)聯(lián)對(duì)象的整個(gè)生命周期都是可用的(在垃圾自動(dòng)回收環(huán)境下也不會(huì)導(dǎo)致資源不可回收)泻蚊。
objc_setAssociatedObject
/**
* Sets an associated value for a given object using a given key and association policy.
*
* @param object The source object for the association.
* @param key The key for the association.
* @param value The value to associate with the key key for object. Pass nil to clear an existing association.
* @param policy The policy for the association. For possible values, see “Associative Object Behaviors.”
*
* @see objc_setAssociatedObject
* @see objc_removeAssociatedObjects
*/
OBJC_EXPORT void
objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
id _Nullable value, objc_AssociationPolicy policy)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);
創(chuàng)建關(guān)聯(lián)要使用到Objective-C的運(yùn)行時(shí)函數(shù):objc_setAssociatedObject來把一個(gè)對(duì)象與另外一個(gè)對(duì)象進(jìn)行關(guān)聯(lián)躲舌。該函數(shù)需要四個(gè)參數(shù):源對(duì)象,關(guān)鍵字性雄,關(guān)聯(lián)的對(duì)象和一個(gè)關(guān)聯(lián)策略孽糖。當(dāng)然枯冈,此處的關(guān)鍵字和關(guān)聯(lián)策略是需要進(jìn)一步討論的。
■ 關(guān)鍵字是一個(gè)void類型的指針办悟。每一個(gè)關(guān)聯(lián)的關(guān)鍵字必須是唯一的。通常都是會(huì)采用靜態(tài)變量來作為關(guān)鍵字滩褥。
■ 關(guān)聯(lián)策略表明了相關(guān)的對(duì)象是通過賦值病蛉,保留引用還是復(fù)制的方式進(jìn)行關(guān)聯(lián)的;還有這種關(guān)聯(lián)是原子的還是非原子的瑰煎。這里的關(guān)聯(lián)策略和聲明屬性時(shí)的很類似铺然。這種關(guān)聯(lián)策略是通過使用預(yù)先定義好的常量來表示的。
objc_getAssociatedObject
/**
* Returns the value associated with a given object for a given key.
*
* @param object The source object for the association.
* @param key The key for the association.
*
* @return The value associated with the key \e key for \e object.
*
* @see objc_setAssociatedObject
*/
OBJC_EXPORT id _Nullable
objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);
獲取相關(guān)聯(lián)的對(duì)象
objc_removeAssociatedObjects
/**
* Removes all associations for a given object.
*
* @param object An object that maintains associated objects.
*
* @note The main purpose of this function is to make it easy to return an object
* to a "pristine state”. You should not use this function for general removal of
* associations from objects, since it also removes associations that other clients
* may have added to the object. Typically you should use \c objc_setAssociatedObject
* with a nil value to clear an association.
*
* @see objc_setAssociatedObject
* @see objc_getAssociatedObject
*/
OBJC_EXPORT void
objc_removeAssociatedObjects(id _Nonnull object)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);
使用函數(shù)objc_removeAssociatedObjects可以斷開所有關(guān)聯(lián)酒甸。通常情況下不建議使用這個(gè)函數(shù)魄健,因?yàn)樗麜?huì)斷開所有關(guān)聯(lián)。只有在需要把對(duì)象恢復(fù)到“原始狀態(tài)”的時(shí)候才會(huì)使用這個(gè)函數(shù)插勤。
斷開關(guān)聯(lián)是使用objc_setAssociatedObject函數(shù)沽瘦,value傳入nil值即可。
- 開源許可證GPL农尖、BSD析恋、MIT、Mozilla盛卡、Apache和LGPL的區(qū)別
-
UML類圖
UML類圖 Xcode內(nèi)部標(biāo)明API信息的簡(jiǎn)單宏定義
NS_AVAILABLE_IOS(5_0)
這個(gè)方法可以在iOS5.0及以后的版本中使用,如果在比5.0更老的版本中調(diào)用這個(gè)方法滓技,就會(huì)引起崩潰哩牍。
NS_DEPRECATED_IOS(2_0, 6_0)
這個(gè)宏中有兩個(gè)版本號(hào)。前面一個(gè)表明了這個(gè)方法被引入時(shí)的iOS版本殖属,后面一個(gè)表明它被廢棄時(shí)的iOS版本姐叁。被廢棄并不是指這個(gè)方法就不存在了,只是意味著我們應(yīng)當(dāng)開始考慮將相關(guān)代碼遷移到新的API上去了洗显。
NS_DEPRECATED(NA, NA, 5_0, 7_0)
這句表示iOS 5.0引用外潜,7.0就廢棄了;NA 表示缺省挠唆,參數(shù)無效不用填处窥。
NS_AVAILABLE(10_8, 6_0)
這個(gè)宏告訴我們這方法分別隨Mac OS 10.8和iOS 6.0被引入。
NS_DEPRECATED(10_0, 10_6, 2_0, 4_0)
這個(gè)方法隨Mac OS 10.0和iOS 2.0被引入玄组,在Mac OS 10.6和iOS 4.0后被廢棄滔驾。
NS_CLASS_AVAILABLE(10_11, 9_0)
這個(gè)類分別隨Mac OS 10.11和iOS9.0被引入谒麦。
NS_ENUM_AVAILABLE(10_11, 9_0)
這個(gè)枚舉分別隨Mac OS 10.11和iOS9.0被引入。
__OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0,__MAC_10_5,__IPHONE_NA,__IPHONE_NA)
os x 10.0 開始引進(jìn)這個(gè)方法 10.5之后廢棄了哆致,ios上從來沒只支持過绕德。
__TVOS_PROHIBITED
表示TVOS 禁止使用
參考:
簡(jiǎn)析KVOController實(shí)現(xiàn)原理
FBKVOController詳解
如何優(yōu)雅地使用 KVO
《iOS 7 Programming Pushing the Limits》系列:你可能不知道的ObjC技巧
開源許可證GPL、BSD摊阀、MIT耻蛇、Mozilla、Apache和LGPL的區(qū)別
利用plantuml繪制類圖
plantuml_Skinparam command
[Objective-C]關(guān)聯(lián)(objc_setAssociatedObject胞此、objc_getAssociatedObject臣咖、objc_removeAssociatedObjects)
iOS系統(tǒng)庫(kù)頭文件中NS_AVAILABLE相關(guān)
iOS官方文檔閱讀
iOS開發(fā)的一些奇巧淫技3
NSMapTable: 不只是一個(gè)能放weak指針的 NSDictionary
Apple - NSMapTable
iOS - 基礎(chǔ)集合類
iOS中保證線程安全的幾種方式與性能對(duì)比
iOS 字典的 addEntriesFromDictionary使用注意點(diǎn)