OC底層文章彙總
在OC底層探索(十六) KVO底層原理文章中分析了KVO的原理,我們實作自定義的KVO。
建立NSObject分類
- .h檔案
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef void(^QLJKVOBlock)(id observer,NSString *keyPath,id oldValue,id newValue);
@interface NSObject (QLJKVO)
- (void)QLJ_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath block:(QLJKVOBlock)block;
- (void)QLJ_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
@end
NS_ASSUME_NONNULL_END
- .m檔案
#import "NSObject+QLJKVO.h"
#import <objc/message.h>
static NSString *const kQLJKVOPrefix = @"QLJKVONotifying_";
static NSString *const kQLJKVOAssiociateKey = @"kQLJKVO_AssiociateKey";
@interface QLJInfo : NSObject
@property (nonatomic, weak) NSObject *observer;
@property (nonatomic, copy) NSString *keyPath;
@property (nonatomic, copy) QLJKVOBlock handleBlock;
@end
@implementation QLJInfo
- (instancetype)initWitObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath handleBlock:(QLJKVOBlock)block{
if (self=[super init]) {
_observer = observer;
_keyPath = keyPath;
_handleBlock = block;
}
return self;
}
@end
@implementation NSObject (QLJKVO)
//- (void)dealloc{
// Class superClass = [self class];
// object_setClass(self, superClass);
//}
+ (BOOL)kc_hookOrigInstanceMenthod:(SEL)oriSEL newInstanceMenthod:(SEL)swizzledSEL {
Class cls = self;
Method oriMethod = class_getInstanceMethod(cls, oriSEL);
Method swiMethod = class_getInstanceMethod(cls, swizzledSEL);
if (!swiMethod) {
return NO;
}
if (!oriMethod) {
class_addMethod(cls, oriSEL, method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));
method_setImplementation(swiMethod, imp_implementationWithBlock(^(id self, SEL _cmd){ }));
}
BOOL didAddMethod = class_addMethod(cls, oriSEL, method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));
if (didAddMethod) {
class_replaceMethod(cls, swizzledSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
}else{
method_exchangeImplementations(oriMethod, swiMethod);
}
return YES;
}
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self kc_hookOrigInstanceMenthod:NSSelectorFromString(@"dealloc") newInstanceMenthod:@selector(myDealloc)];
});
}
- (void)myDealloc{
Class superClass = [self class];
object_setClass(self, superClass);
[self myDealloc];
}
- (void)QLJ_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath block:(QLJKVOBlock)block{
// 1: 驗證是否存在setter方法 : 不讓執行個體進來
[self judgeSetterMethodFromKeyPath:keyPath];
// 2: 動态生成子類
Class newClass = [self createChildClassWithKeyPath:keyPath];
// 3: isa的指向 : QLJKVONotifying_QLJPerson
object_setClass(self, newClass);
// 4: 儲存資訊
QLJInfo *info = [[QLJInfo alloc] initWitObserver:observer forKeyPath:keyPath handleBlock:block];
NSMutableArray *mArray = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kQLJKVOAssiociateKey));
if (!mArray) {
mArray = [NSMutableArray arrayWithCapacity:1];
objc_setAssociatedObject(self, (__bridge const void * _Nonnull)(kQLJKVOAssiociateKey), mArray, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
[mArray addObject:info];
}
- (void)QLJ_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath{
NSMutableArray *observerArr = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kQLJKVOAssiociateKey));
if (observerArr.count<=0) {
return;
}
for (QLJInfo *info in observerArr) {
if ([info.keyPath isEqualToString:keyPath]) {
[observerArr removeObject:info];
objc_setAssociatedObject(self, (__bridge const void * _Nonnull)(kQLJKVOAssiociateKey), observerArr, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
break;
}
}
if (observerArr.count<=0) {
// 指回給父類
Class superClass = [self class];
object_setClass(self, superClass);
}
}
#pragma mark - 驗證是否存在setter方法
- (void)judgeSetterMethodFromKeyPath:(NSString *)keyPath{
Class superClass = object_getClass(self);
SEL setterSeletor = NSSelectorFromString(setterForGetter(keyPath));
Method setterMethod = class_getInstanceMethod(superClass, setterSeletor);
if (!setterMethod) {
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"老鐵沒有目前%@的setter",keyPath] userInfo:nil];
}
}
#pragma mark -
- (Class)createChildClassWithKeyPath:(NSString *)keyPath{
NSString *oldClassName = NSStringFromClass([self class]);
NSString *newClassName = [NSString stringWithFormat:@"%@%@",kQLJKVOPrefix,oldClassName];
Class newClass = NSClassFromString(newClassName);
// 防止重複建立生成新類
if (newClass) return newClass;
/**
* 如果記憶體不存在,建立生成
* 參數一: 父類
* 參數二: 新類的名字
* 參數三: 新類的開辟的額外空間
*/
// 2.1 : 申請類
newClass = objc_allocateClassPair([self class], newClassName.UTF8String, 0);
// 2.2 : 注冊類
objc_registerClassPair(newClass);
// 2.3.1 : 添加class : class的指向是QLJPerson
SEL classSEL = NSSelectorFromString(@"class");
Method classMethod = class_getInstanceMethod([self class], classSEL);
const char *classTypes = method_getTypeEncoding(classMethod);
class_addMethod(newClass, classSEL, (IMP)QLJ_class, classTypes);
// 2.3.2 : 添加setter
SEL setterSEL = NSSelectorFromString(setterForGetter(keyPath));
Method setterMethod = class_getInstanceMethod([self class], setterSEL);
const char *setterTypes = method_getTypeEncoding(setterMethod);
class_addMethod(newClass, setterSEL, (IMP)QLJ_setter, setterTypes);
// 2.3.3 : 添加dealloc
SEL deallocSEL = NSSelectorFromString(@"dealloc");
Method deallocMethod = class_getInstanceMethod([self class], deallocSEL);
const char *deallocTypes = method_getTypeEncoding(deallocMethod);
class_addMethod(newClass, deallocSEL, (IMP)QLJ_dealloc, deallocTypes);
// Method m1 = class_getInstanceMethod([self class], NSSelectorFromString(@"dealloc"));
// Method m2 = class_getInstanceMethod([self class], @selector(myDealloc));
// method_exchangeImplementations(m1, m2);
// method_exchangeImplementations(m1, m2);
return newClass;
}
static void QLJ_dealloc(id self,SEL _cmd){
// Class superClass = [self class];
// object_setClass(self, superClass);
}
static void QLJ_setter(id self,SEL _cmd,id newValue){
NSLog(@"來了:%@",newValue);
NSString *keyPath = getterForSetter(NSStringFromSelector(_cmd));
id oldValue = [self valueForKey:keyPath];
// 4: 消息轉發 : 轉發給父類
// 改變父類的值 --- 可以強制類型轉換
void (*QLJ_msgSendSuper)(void *,SEL , id) = (void *)objc_msgSendSuper;
// void /* struct objc_super *super, SEL op, ... */
struct objc_super superStruct = {
.receiver = self,
.super_class = class_getSuperclass(object_getClass(self)),
};
//objc_msgSendSuper(&superStruct,_cmd,newValue)
QLJ_msgSendSuper(&superStruct,_cmd,newValue);
// 5: 資訊資料回調
NSMutableArray *mArray = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kQLJKVOAssiociateKey));
for (QLJInfo *info in mArray) {
if ([info.keyPath isEqualToString:keyPath] && info.handleBlock) {
info.handleBlock(info.observer, keyPath, oldValue, newValue);
}
}
}
Class QLJ_class(id self,SEL _cmd){
return class_getSuperclass(object_getClass(self));
}
#pragma mark - 從get方法擷取set方法的名稱 key ===>>> setKey:
static NSString *setterForGetter(NSString *getter){
if (getter.length <= 0) { return nil;}
NSString *firstString = [[getter substringToIndex:1] uppercaseString];
NSString *leaveString = [getter substringFromIndex:1];
return [NSString stringWithFormat:@"set%@%@:",firstString,leaveString];
}
#pragma mark - 從set方法擷取getter方法的名稱 set<Key>:===> key
static NSString *getterForSetter(NSString *setter){
if (setter.length <= 0 || ![setter hasPrefix:@"set"] || ![setter hasSuffix:@":"]) { return nil;}
NSRange range = NSMakeRange(3, setter.length-4);
NSString *getter = [setter substringWithRange:range];
NSString *firstString = [[getter substringToIndex:1] lowercaseString];
return [getter stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:firstString];
}
@end