查看: 2959|回复: 0

[手机开发] iOS学习笔记--KVO的代码实现

发表于 2018-1-19 08:00:03

说明注释全在代码里,还有RunTime的方法说明,如果我表达的有歧义,请看API英文说明

// NSObject+KVO.h

  1. #import <Foundation/Foundation.h>
  2. typedef void(^ObservingBlock) (id observedObject,NSString *observedkey,id oldValue,id newValue);
  3. @interface NSObject (KVO)
  4. //自定义添加观察者
  5. - (void)addCustomObserver:(NSObject *)observer
  6. key:(NSString *)key
  7. block:(ObservingBlock)block;
  8. //移除自定义观察者
  9. - (void)removeCustomObserver:(NSObject *)observer forKey:(NSString *)key;
  10. @end
复制代码

//NSObject+KVO.m 里的全部代码,如下

  1. #import "NSObject+KVO.h"
  2. #include <objc/runtime.h>
  3. #include <objc/message.h>
  4. NSString *const KVO_CustomPrefix = @"KVO_CustomPrefix_";
  5. NSString *const KVO_CustomObserverKey = @"KVO_CustomObserverKey";
复制代码
  1. //设置观察者配置信息
  2. @interface ObserverInfo : NSObject
  3. @property (nonatomic,strong) id observer;
  4. @property (nonatomic,copy) NSString *key;
  5. @property (nonatomic,copy) ObservingBlock block;
  6. @end
  7. @implementation ObserverInfo
  8. - (instancetype)initWithObserver:(NSObject *)observer key:(NSString *)key block:(ObservingBlock)block
  9. {
  10. self = [super init];
  11. if (self) {
  12. _observer = observer;
  13. _key = key;
  14. _block = block;
  15. }
  16. return self;
  17. }
  18. @end
复制代码
  1. //setter 转getter setName:->name
  2. static NSString *getterFromSetter(NSString *setter)
  3. {
  4. if (setter.length <= 0 && ![setter hasPrefix:@"set"] && ![setter hasSuffix:@":"]) {
  5. return nil;
  6. }
  7. NSString *pNameStr = [setter substringWithRange:NSMakeRange(3, setter.length-3-1)];
  8. NSString *first = [[pNameStr substringToIndex:1] lowercaseString];
  9. return [pNameStr stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:first];
  10. }
  11. //setter 转getter name->setName:
  12. static NSString *setterFromGetter(NSString *getter)
  13. {
  14. NSString *first = [[getter substringToIndex:1] uppercaseString];
  15. NSString *upString = [getter stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:first];
  16. return [NSString stringWithFormat:@"set%@:",upString];
  17. }
  18. static void kvo_setter(id self,SEL _cmd,id newValue){
  19. NSString *setterName = NSStringFromSelector(_cmd);
  20. NSString *getterName = getterFromSetter(setterName);
  21. if (!getterName) {
  22. NSString *reason = [NSString stringWithFormat:@"Object %@ does not have setter %@", self, setterName];
  23. @throw [NSException exceptionWithName:NSInvalidArgumentException reason:reason userInfo:nil];
  24. return;
  25. }
  26. id oldValue = [self valueForKey:getterName];
  27. struct objc_super superclass = {
  28. .receiver = self,
  29. .super_class= class_getSuperclass(object_getClass(self))
  30. };
  31. /*These functions must be cast to an appropriate function pointer type
  32. * before being called
  33. */
  34. void (*objc_msgSendSuperCasted)(void *,SEL, id) = (void *)objc_msgSendSuper;
  35. objc_msgSendSuperCasted(&superclass,_cmd,newValue);
  36. NSMutableArray *observers = objc_getAssociatedObject(self, (__bridge const void *)(KVO_CustomObserverKey));
  37. for (ObserverInfo *each in observers) {
  38. if ([each.key isEqualToString:getterName]) {
  39. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  40. each.block(self, getterName, oldValue, newValue);
  41. });
  42. }
  43. }
  44. }
  45. static Class kvo_superClass(id self, SEL _cmd)
  46. {
  47. return class_getSuperclass(object_getClass(self));
  48. }
复制代码
  1. @implementation NSObject (KVO)
  2. - (void)addCustomObserver:(NSObject *)observer
  3. key:(NSString *)key
  4. block:(ObservingBlock)block
  5. {
  6. //伪代码MethodList:[Method:{SEL:IMP},Method:{SEL:IMP}....]
  7. SEL setter = NSSelectorFromString(setterFromGetter(key));
  8. // Returns a specified instance method for a given class.
  9. Method setterMethod = class_getInstanceMethod([self class], setter);
  10. //先判断是否有Setter Method
  11. if (!setterMethod) {
  12. NSString *reason = [NSString stringWithFormat:@"Object %@ does not have a setter for key %@", self, key];
  13. @throw [NSException exceptionWithName:NSInvalidArgumentException reason:reason userInfo:nil];
  14. return;
  15. }
  16. Class class = object_getClass(self);
  17. NSString *className = NSStringFromClass(class);
  18. //判断类是否已经添加过观察者,判断方式就是前缀
  19. if (![className hasPrefix:KVO_CustomPrefix]) {
  20. //获取新注册的类
  21. class = [self makeDeriveKVOClassWithOriginClassName:className];
  22. //把对象的class指针指向新注册的类
  23. object_setClass(self, class);
  24. }
  25. //如果本类没有Setter SEL实现--IMP
  26. if (![self hasSelector:setter]) {
  27. //Returns a string describing a method's parameter and return types.
  28. const char *types = method_getTypeEncoding(setterMethod);
  29. //Adds a new method to a class with a given name and implementation.
  30. class_addMethod(class, setter, (IMP)kvo_setter, types);
  31. }
  32. //初始化观察者
  33. ObserverInfo *info = [[ObserverInfo alloc]initWithObserver:observer key:key block:block];
  34. //获取属性变量observers
  35. NSMutableArray *observers = objc_getAssociatedObject(self, (__bridge const void*)(KVO_CustomObserverKey));
  36. if (!observers) {
  37. //如果属性变量不存在 就重新初始化,并添加到类属性列表里
  38. observers = [NSMutableArray array];
  39. objc_setAssociatedObject(self, (__bridge const void *)(KVO_CustomObserverKey), observers, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  40. }
  41. //把初始化的观察者信息对象添加到观察者里
  42. [observers addObject:info];
  43. }
  44. - (void)removeCustomObserver:(NSObject *)observer forKey:(NSString *)key;
  45. {
  46. //获取属性变量Observers
  47. NSMutableArray *observes = objc_getAssociatedObject(self, (__bridge const void*)(KVO_CustomObserverKey));
  48. ObserverInfo *removeObserver;
  49. //遍历观察者列表,找到一致的对象,移除
  50. for (ObserverInfo *each in observes) {
  51. if (each.observer == observer && [each.key isEqualToString:key]) {
  52. removeObserver = each;
  53. break;
  54. }
  55. }
  56. [observes removeObject:removeObserver];
  57. }
  58. - (Class)makeDeriveKVOClassWithOriginClassName:(NSString *)originClassName
  59. {
  60. //根据原类名拼接出派生类
  61. NSString *deriveClassName = [KVO_CustomPrefix stringByAppendingString:originClassName];
  62. Class deriveClass = NSClassFromString(deriveClassName);
  63. if (deriveClass) {
  64. return deriveClass;
  65. }
  66. //获取本类
  67. Class originClass = object_getClass(self);
  68. /*Creates a new class and metaclass.
  69. * @param superclass
  70. * @param name
  71. * @param extraBytes
  72. */
  73. Class newClass = objc_allocateClassPair(originClass, deriveClassName.UTF8String, 0);
  74. //为派生类的class方法添加实现
  75. Method method = class_getInstanceMethod(originClass, @selector(class));
  76. const char *types = method_getTypeEncoding(method);
  77. class_addMethod(newClass, @selector(class), (IMP)kvo_superClass, types);
  78. //注册到RunTime
  79. objc_registerClassPair(newClass);
  80. return newClass;
  81. }
  82. //判断是否有这个方法
  83. - (BOOL)hasSelector:(SEL)selector
  84. {
  85. Class clazz = object_getClass(self);
  86. unsigned int methodCount = 0;
  87. Method* methodList = class_copyMethodList(clazz, &methodCount);
  88. for (unsigned int i = 0; i < methodCount; i++) {
  89. SEL thisSelector = method_getName(methodList[i]);
  90. if (thisSelector == selector) {
  91. free(methodList);
  92. return YES;
  93. }
  94. }
  95. free(methodList);
  96. return NO;
  97. }
  98. @end
复制代码


回复

使用道具 举报