簡介
鍵值觀察是模型 - 視圖 - 控制器應(yīng)用程序(MVC)中各層之間通信的一種特別有用的技術(shù)。
KVOController以Cocoa經(jīng)過時間考驗的鍵值觀察實現(xiàn)為基礎(chǔ)。 它提供了一個簡單、現(xiàn)代的API,同時也是線程安全的。
如果你在項目中有使用 KVO ,那么 KVOController 絕對是個好選擇。它是 facebook 開源的一個 KVO 增強框架。
有以下幾個特性:
使用 Blocks 、自定義 Actions 或者 NSKeyValueObserving 回調(diào)進(jìn)行通知
觀測者移除時無異常
控制器 dealloc 時隱式的觀測者移除
提升使用 NSKeyValueObservingInitial 的性能
線程安全并提供在觀測者恢復(fù)時額外的保護(hù)
開發(fā)環(huán)境
KVOController利用了當(dāng)前Objective-C中runtime的優(yōu)勢,以及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)行安裝,請將以下內(nèi)容添加到項目Podfile中:
pod 'KVOController'
要使用Carthage進(jìn)行安裝,請將以下內(nèi)容添加到項目Cartfile中:
github "facebook/KVOController"
或者,將FBKVOController.h和FBKVOController.m拖放到Xcode項目中, 并同意在需要時復(fù)制文件。 對于iOS應(yīng)用程序,您可以選擇鏈接KVOController項目的靜態(tài)庫目標(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ī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()
}
測試
單元測試包括使用CocoaPods來管理依賴項。 如果您尚未安裝CocoaPods,請安裝它們。 然后,在命令行中,導(dǎo)航到根KVOController目錄并鍵入:
pod install
這將在OCHamcrest和OCMockito上安裝和添加測試依賴項。 重新打開Xcode的KVOController工作區(qū)和Test,?U。
開源許可證
KVOController根據(jù) BSD許可證 發(fā)布。 有關(guān)詳細(xì)信息,請參閱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是對Cocoa中KVO進(jìn)行了封裝,整個框架只有兩個實現(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實現(xiàn)流程:
Observer會創(chuàng)建一個FBKVOController的屬性;
FBKVOController中包含一個NSMapTable的成員屬性,用來存儲observer的KVO信息;
FBKVOController創(chuàng)建一個_FBKVOInfo類型的實例,實例中存儲了和KVO操作相關(guān)的信息(keypath等),然后將需要觀察的對象Target作為Key,_FBKVOInfo的實例加入數(shù)組(對同一個Target的不同keypath的多次KVO操作)并把數(shù)組作為Value,存入步驟2中的mapTable中;
FBKVOController會調(diào)用_FBKVOSharedController的單例中的方法,同時將步驟3創(chuàng)建的info和觀察的target傳入給這個方法,這個單例進(jìn)行了最終的KVO操作;
-
_FBKVOSharedController的單例調(diào)用系統(tǒng)KVO方法,將自己作為觀察者來觀察Target對象。
Remove Observer
在Observer內(nèi)存被釋放,執(zhí)行dealloc時,其創(chuàng)建的FBKVOController屬性的析構(gòu)方法dealloc會通過KVOInfoMap找到所有KVO的對象,并執(zhí)行移除觀察的操作,十分巧妙的設(shè)計!
但是在使用的過程中還是有一些注意事項的:
首先,F(xiàn)BKVOController使用block來傳遞系統(tǒng)KVO的回調(diào),因此要注意retain cycle。
其次,在使用的過程中,target不能強引用observer,否則也會形成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)行為都會retain(或弱引用)鍵對象而不會拷貝它,與CFDictionary的行為相同而與NSDictionary不同。當(dāng)你需要一個字典,它的鍵沒有實現(xiàn)NSCopying協(xié)議,比如UIView,的時候非常有用。
如果你好奇為什么蘋果”忘記”為NSMapTable增加下標(biāo),你現(xiàn)在知道了。下標(biāo)訪問需要一個id<NSCopying>作為key,對NSMapTable來說這不是強制的。如果不通過一個非法的API協(xié)議或者移除NSCopying協(xié)議來削弱全局下標(biāo),是沒有辦法給它增加下標(biāo)的。
NSMapTable查詢性能只比NSDictionary略微慢一點。如果你需要一個不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)在使用的時候,返回為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的情況下,相對應(yīng)的value會被賦予新值,使用時注意順序
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);
// 控制臺打印如下:
{
CarColor = Red;
CarLogo = BMW;
age = 25;
name = MountainX;
}
- 分類 NSObject+FBKVOController
利用分類和Runtime中的關(guān)聯(lián)機制為NSObject添加了兩個懶加載的屬性:
/**
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在使用時并不會持有被觀察的對象
實現(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;
這種聲明方式在編譯的時候創(chuàng)建一個唯一的指針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來添加.
因為kvo的時候context如果不小心重復(fù)了,會發(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)是指把兩個對象相互關(guān)聯(lián)起來,使得其中的一個對象作為另外一個對象的一部分。
關(guān)聯(lián)特性只有在Mac OS X V10.6以及以后的版本上才是可用的。
使用關(guān)聯(lián),我們可以不用修改類的定義而為其對象增加存儲空間。這在我們無法訪問到類的源碼的時候或者是考慮到二進(jìn)制兼容性的時候是非常有用。
關(guān)聯(lián)是基于關(guān)鍵字的,因此,我們可以為任何對象增加任意多的關(guān)聯(lián),每個都使用不同的關(guān)鍵字即可。關(guān)聯(lián)是可以保證被關(guān)聯(lián)的對象在關(guān)聯(lián)對象的整個生命周期都是可用的(在垃圾自動回收環(huán)境下也不會導(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的運行時函數(shù):objc_setAssociatedObject來把一個對象與另外一個對象進(jìn)行關(guān)聯(lián)。該函數(shù)需要四個參數(shù):源對象,關(guān)鍵字,關(guān)聯(lián)的對象和一個關(guān)聯(lián)策略。當(dāng)然,此處的關(guān)鍵字和關(guān)聯(lián)策略是需要進(jìn)一步討論的。
■ 關(guān)鍵字是一個void類型的指針。每一個關(guān)聯(lián)的關(guān)鍵字必須是唯一的。通常都是會采用靜態(tài)變量來作為關(guān)鍵字。
■ 關(guān)聯(lián)策略表明了相關(guān)的對象是通過賦值,保留引用還是復(fù)制的方式進(jìn)行關(guān)聯(lián)的;還有這種關(guān)聯(lián)是原子的還是非原子的。這里的關(guān)聯(lián)策略和聲明屬性時的很類似。這種關(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)的對象
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)。通常情況下不建議使用這個函數(shù),因為他會斷開所有關(guān)聯(lián)。只有在需要把對象恢復(fù)到“原始狀態(tài)”的時候才會使用這個函數(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信息的簡單宏定義
NS_AVAILABLE_IOS(5_0)
這個方法可以在iOS5.0及以后的版本中使用,如果在比5.0更老的版本中調(diào)用這個方法,就會引起崩潰。
NS_DEPRECATED_IOS(2_0, 6_0)
這個宏中有兩個版本號。前面一個表明了這個方法被引入時的iOS版本,后面一個表明它被廢棄時的iOS版本。被廢棄并不是指這個方法就不存在了,只是意味著我們應(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)
這個宏告訴我們這方法分別隨Mac OS 10.8和iOS 6.0被引入。
NS_DEPRECATED(10_0, 10_6, 2_0, 4_0)
這個方法隨Mac OS 10.0和iOS 2.0被引入,在Mac OS 10.6和iOS 4.0后被廢棄。
NS_CLASS_AVAILABLE(10_11, 9_0)
這個類分別隨Mac OS 10.11和iOS9.0被引入。
NS_ENUM_AVAILABLE(10_11, 9_0)
這個枚舉分別隨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)這個方法 10.5之后廢棄了,ios上從來沒只支持過。
__TVOS_PROHIBITED
表示TVOS 禁止使用
參考:
簡析KVOController實現(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)庫頭文件中NS_AVAILABLE相關(guān)
iOS官方文檔閱讀
iOS開發(fā)的一些奇巧淫技3
NSMapTable: 不只是一個能放weak指針的 NSDictionary
Apple - NSMapTable
iOS - 基礎(chǔ)集合類
iOS中保證線程安全的幾種方式與性能對比
iOS 字典的 addEntriesFromDictionary使用注意點


