Commit a0f3ad78 authored by 李杰's avatar 李杰
Browse files

Refactor the KVO

Showing with 234 additions and 66 deletions
+234 -66
...@@ -16,7 +16,7 @@ Pod::Spec.new do |s| ...@@ -16,7 +16,7 @@ Pod::Spec.new do |s|
# #
s.name = "JJException" s.name = "JJException"
s.version = "0.1.7" s.version = "0.1.8"
s.summary = "Handle the objective-c crash exception." s.summary = "Handle the objective-c crash exception."
# This description is used to generate tags and improve search results. # This description is used to generate tags and improve search results.
...@@ -69,7 +69,7 @@ Pod::Spec.new do |s| ...@@ -69,7 +69,7 @@ Pod::Spec.new do |s|
# When using multiple platforms # When using multiple platforms
s.ios.deployment_target = "8.0" s.ios.deployment_target = "8.0"
s.osx.deployment_target = "10.7" s.osx.deployment_target = "10.8"
s.watchos.deployment_target = "2.0" s.watchos.deployment_target = "2.0"
s.tvos.deployment_target = "9.0" s.tvos.deployment_target = "9.0"
......
No preview for this file type
...@@ -27,61 +27,13 @@ ...@@ -27,61 +27,13 @@
shouldBeEnabled = "Yes" shouldBeEnabled = "Yes"
ignoreCount = "0" ignoreCount = "0"
continueAfterRunningActions = "No" continueAfterRunningActions = "No"
filePath = "JJException/Source/MRC/NSObject+KVOCrash.m" filePath = "JJException/Source/DeallocBlock/NSObject+DeallocBlock.m"
timestampString = "564488427.585709" timestampString = "564822196.511668"
startingColumnNumber = "9223372036854775807" startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807"
startingLineNumber = "133" startingLineNumber = "26"
endingLineNumber = "133" endingLineNumber = "26"
landmarkName = "-clearKVOData" landmarkName = "-dealloc"
landmarkType = "7">
<Locations>
<Location
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "-[KVOObjectContainer clearKVOData]"
moduleName = "JJException"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/jezz/git_project/JJExceptionGithub/JJException/Source/MRC/NSObject+KVOCrash.m"
timestampString = "564488398.348398"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "133"
endingLineNumber = "133"
offsetFromSymbolStart = "55">
</Location>
<Location
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "-[KVOObjectContainer clearKVOData]"
moduleName = "JJException"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/jezz/git_project/JJExceptionGithub/JJException/Source/MRC/NSObject+KVOCrash.m"
timestampString = "564488398.350692"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "133"
endingLineNumber = "133"
offsetFromSymbolStart = "65">
</Location>
</Locations>
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "JJException/Source/MRC/NSObject+KVOCrash.m"
timestampString = "564488427.587878"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "244"
endingLineNumber = "244"
landmarkName = "-kvo_hookDealloc"
landmarkType = "7"> landmarkType = "7">
</BreakpointContent> </BreakpointContent>
</BreakpointProxy> </BreakpointProxy>
......
...@@ -56,6 +56,7 @@ ...@@ -56,6 +56,7 @@
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
_kvoObserver = nil; _kvoObserver = nil;
self.demoString1 = @"11";
}); });
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#import "JJExceptionProxy.h" #import "JJExceptionProxy.h"
static const char DeallocKVOKey; static const char DeallocKVOKey;
static const char ObserverDeallocKVOKey;
/** /**
Record the kvo object Record the kvo object
...@@ -51,6 +52,7 @@ static const char DeallocKVOKey; ...@@ -51,6 +52,7 @@ static const char DeallocKVOKey;
@end @end
@interface KVOObjectContainer : NSObject @interface KVOObjectContainer : NSObject
/** /**
...@@ -129,7 +131,7 @@ static const char DeallocKVOKey; ...@@ -129,7 +131,7 @@ static const char DeallocKVOKey;
[super dealloc]; [super dealloc];
} }
- (void)clearKVOData{ - (void)cleanKVOData{
for (KVOObjectItem* item in self.kvoObjectSet) { for (KVOObjectItem* item in self.kvoObjectSet) {
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector" #pragma clang diagnostic ignored "-Wundeclared-selector"
...@@ -151,14 +153,66 @@ static const char DeallocKVOKey; ...@@ -151,14 +153,66 @@ static const char DeallocKVOKey;
@end @end
@interface JJObserverContainer : NSObject
@property (nonatomic,readwrite,strong) NSHashTable* observers;
/**
Associated owner object
*/
@property(nonatomic,readwrite,unsafe_unretained)NSObject* whichObject;
- (void)addObserver:(KVOObjectItem *)observer;
- (void)removeObserver:(KVOObjectItem *)observer;
@end
@implementation JJObserverContainer
- (instancetype)init
{
self = [super init];
if (self) {
self.observers = [NSHashTable hashTableWithOptions:NSMapTableWeakMemory];
}
return self;
}
- (void)addObserver:(KVOObjectItem *)observer
{
@synchronized (self) {
[self.observers addObject:observer];
}
}
- (void)removeObserver:(KVOObjectItem *)observer
{
@synchronized (self) {
[self.observers removeObject:observer];
}
}
- (void)cleanObservers{
for (KVOObjectItem* item in self.observers) {
[self.whichObject removeObserver:item.observer forKeyPath:item.keyPath];
}
}
- (void)dealloc{
self.whichObject = nil;
[self.observers release];
[super dealloc];
}
@end
@implementation NSObject (KVOCrash) @implementation NSObject (KVOCrash)
+ (void)jj_swizzleKVOCrash{ + (void)jj_swizzleKVOCrash{
swizzleInstanceMethod([self class], @selector(addObserver:forKeyPath:options:context:), @selector(hookAddObserver:forKeyPath:options:context:)); swizzleInstanceMethod([self class], @selector(addObserver:forKeyPath:options:context:), @selector(hookAddObserver:forKeyPath:options:context:));
swizzleInstanceMethod([self class], @selector(removeObserver:forKeyPath:), @selector(hookRemoveObserver:forKeyPath:)); swizzleInstanceMethod([self class], @selector(removeObserver:forKeyPath:), @selector(hookRemoveObserver:forKeyPath:));
swizzleInstanceMethod([self class], @selector(removeObserver:forKeyPath:context:), @selector(hookRemoveObserver:forKeyPath:context:)); swizzleInstanceMethod([self class], @selector(removeObserver:forKeyPath:context:), @selector(hookRemoveObserver:forKeyPath:context:));
//Swizzle kvo dealloc
swizzleInstanceMethod([self class], @selector(dealloc), @selector(kvo_hookDealloc));
} }
- (void)hookAddObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{ - (void)hookAddObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{
...@@ -191,7 +245,22 @@ static const char DeallocKVOKey; ...@@ -191,7 +245,22 @@ static const char DeallocKVOKey;
[self hookAddObserver:observer forKeyPath:keyPath options:options context:context]; [self hookAddObserver:observer forKeyPath:keyPath options:options context:context];
} }
JJObserverContainer* observerContainer = objc_getAssociatedObject(observer,&ObserverDeallocKVOKey);
if (!observerContainer) {
observerContainer = [JJObserverContainer new];
[observerContainer setWhichObject:self];
[observerContainer addObserver:item];
objc_setAssociatedObject(observer, &ObserverDeallocKVOKey, observerContainer, OBJC_ASSOCIATION_RETAIN);
[observerContainer release];
}else{
[observerContainer addObserver:item];
}
[item release]; [item release];
jj_swizzleDeallocIfNeeded(self.class);
jj_swizzleDeallocIfNeeded(observer.class);
} }
- (void)hookRemoveObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(void*)context{ - (void)hookRemoveObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(void*)context{
...@@ -236,19 +305,16 @@ static const char DeallocKVOKey; ...@@ -236,19 +305,16 @@ static const char DeallocKVOKey;
/** /**
* Hook the kvo object dealloc and to clean the kvo array, * Hook the kvo object dealloc and to clean the kvo array
* And show the more kvo object info to the user
*/ */
- (void)kvo_hookDealloc{ - (void)jj_cleanKVO{
KVOObjectContainer* objectContainer = objc_getAssociatedObject(self, &DeallocKVOKey); KVOObjectContainer* objectContainer = objc_getAssociatedObject(self, &DeallocKVOKey);
[objectContainer cleanKVOData];
if (objectContainer) { JJObserverContainer* observerContainer = objc_getAssociatedObject(self, &ObserverDeallocKVOKey);
[objectContainer clearKVOData]; [observerContainer cleanObservers];
}
//Invoke the origin dealloc
[self kvo_hookDealloc];
} }
@end @end
...@@ -8,6 +8,11 @@ ...@@ -8,6 +8,11 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
/*
* JJSwizzledIMPBlock assist variable
*/
typedef void (*JJSwizzleOriginalIMP)(void /* id, SEL, ... */ ); typedef void (*JJSwizzleOriginalIMP)(void /* id, SEL, ... */ );
@interface JJSwizzleObject : NSObject @interface JJSwizzleObject : NSObject
...@@ -20,16 +25,64 @@ typedef void (*JJSwizzleOriginalIMP)(void /* id, SEL, ... */ ); ...@@ -20,16 +25,64 @@ typedef void (*JJSwizzleOriginalIMP)(void /* id, SEL, ... */ );
typedef id (^JJSwizzledIMPBlock)(JJSwizzleObject* swizzleInfo); typedef id (^JJSwizzledIMPBlock)(JJSwizzleObject* swizzleInfo);
/*
* JJSwizzledIMPBlock assist variable
*/
/**
* Swizzle Class Method
@param cls Class
@param originSelector originSelector
@param swizzleSelector swizzleSelector
*/
void swizzleClassMethod(Class cls, SEL originSelector, SEL swizzleSelector); void swizzleClassMethod(Class cls, SEL originSelector, SEL swizzleSelector);
/**
* Swizzle Instance Class Method
@param cls Class
@param originSelector originSelector
@param swizzleSelector swizzleSelector
*/
void swizzleInstanceMethod(Class cls, SEL originSelector, SEL swizzleSelector); void swizzleInstanceMethod(Class cls, SEL originSelector, SEL swizzleSelector);
/**
* Only swizzle the current class,not swizzle all class
* perform jj_cleanKVO selector before the origin dealloc
@param class Class
*/
void jj_swizzleDeallocIfNeeded(Class class);
/**
Swizzle the NSObject Extension
*/
@interface NSObject (SwizzleHook) @interface NSObject (SwizzleHook)
/**
Swizzle Class Method
@param originSelector originSelector
@param swizzleSelector swizzleSelector
*/
+ (void)jj_swizzleClassMethod:(SEL)originSelector withSwizzleMethod:(SEL)swizzleSelector; + (void)jj_swizzleClassMethod:(SEL)originSelector withSwizzleMethod:(SEL)swizzleSelector;
/**
Swizzle Instance Method
@param originSelector originSelector
@param swizzleSelector swizzleSelector
*/
- (void)jj_swizzleInstanceMethod:(SEL)originSelector withSwizzleMethod:(SEL)swizzleSelector; - (void)jj_swizzleInstanceMethod:(SEL)originSelector withSwizzleMethod:(SEL)swizzleSelector;
/**
Swizzle instance method to the block target
@param originSelector originSelector
@param swizzledBlock block
*/
- (void)jj_swizzleInstanceMethod:(SEL)originSelector withSwizzledBlock:(JJSwizzledIMPBlock)swizzledBlock; - (void)jj_swizzleInstanceMethod:(SEL)originSelector withSwizzledBlock:(JJSwizzledIMPBlock)swizzledBlock;
@end @end
...@@ -8,10 +8,13 @@ ...@@ -8,10 +8,13 @@
#import "NSObject+SwizzleHook.h" #import "NSObject+SwizzleHook.h"
#import <objc/runtime.h> #import <objc/runtime.h>
#import <objc/message.h>
#import <libkern/OSAtomic.h> #import <libkern/OSAtomic.h>
typedef IMP (^JJSWizzleImpProvider)(void); typedef IMP (^JJSWizzleImpProvider)(void);
static const char jjSwizzledDeallocKey;
@interface JJSwizzleObject() @interface JJSwizzleObject()
@property (nonatomic,readwrite,copy) JJSWizzleImpProvider impProviderBlock; @property (nonatomic,readwrite,copy) JJSWizzleImpProvider impProviderBlock;
@property (nonatomic,readwrite,assign) SEL selector; @property (nonatomic,readwrite,assign) SEL selector;
...@@ -88,6 +91,60 @@ void swizzleInstanceMethod(Class cls, SEL originSelector, SEL swizzleSelector){ ...@@ -88,6 +91,60 @@ void swizzleInstanceMethod(Class cls, SEL originSelector, SEL swizzleSelector){
} }
} }
// a class doesn't need dealloc swizzled if it or a superclass has been swizzled already
BOOL jj_requiresDeallocSwizzle(Class class)
{
BOOL swizzled = NO;
for ( Class currentClass = class; !swizzled && currentClass != nil; currentClass = class_getSuperclass(currentClass) ) {
swizzled = [objc_getAssociatedObject(currentClass, &jjSwizzledDeallocKey) boolValue];
}
return !swizzled;
}
void jj_swizzleDeallocIfNeeded(Class class)
{
static SEL deallocSEL = NULL;
static SEL cleanupSEL = NULL;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
deallocSEL = sel_getUid("dealloc");
cleanupSEL = sel_getUid("jj_cleanKVO");
});
@synchronized (class) {
if ( !jj_requiresDeallocSwizzle(class) ) {
return;
}
objc_setAssociatedObject(class, &jjSwizzledDeallocKey, @(YES), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
Method dealloc = class_getInstanceMethod(class, deallocSEL);
if ( dealloc == NULL ) {
Class superclass = class_getSuperclass(class);
class_addMethod(class, deallocSEL, imp_implementationWithBlock(^(__unsafe_unretained id self) {
((void(*)(id, SEL))objc_msgSend)(self, cleanupSEL);
struct objc_super superStruct = (struct objc_super){ self, superclass };
((void (*)(struct objc_super*, SEL))objc_msgSendSuper)(&superStruct, deallocSEL);
}), method_getTypeEncoding(dealloc));
}else{
__block IMP deallocIMP = method_setImplementation(dealloc, imp_implementationWithBlock(^(__unsafe_unretained id self) {
((void(*)(id, SEL))objc_msgSend)(self, cleanupSEL);
((void(*)(id, SEL))deallocIMP)(self, deallocSEL);
}));
}
}
@implementation NSObject (SwizzleHook) @implementation NSObject (SwizzleHook)
void __JJ_SWIZZLE_BLOCK(Class classToSwizzle,SEL selector,JJSwizzledIMPBlock impBlock){ void __JJ_SWIZZLE_BLOCK(Class classToSwizzle,SEL selector,JJSwizzledIMPBlock impBlock){
......
...@@ -36,6 +36,29 @@ ...@@ -36,6 +36,29 @@
- (NSString *)stringByAppendingString:(NSString *)aString; - (NSString *)stringByAppendingString:(NSString *)aString;
``` ```
* NSAttributedString
```
- (instancetype)initWithString:(NSString *)str;
- (NSAttributedString *)attributedSubstringFromRange:(NSRange)range;
- (nullable id)attribute:(NSAttributedStringKey)attrName atIndex:(NSUInteger)location effectiveRange:(nullable NSRangePointer)range;
- (void)enumerateAttribute:(NSAttributedStringKey)attrName inRange:(NSRange)enumerationRange options:(NSAttributedStringEnumerationOptions)opts usingBlock:(void (NS_NOESCAPE ^)(id _Nullable value, NSRange range, BOOL *stop))block;
- (void)enumerateAttributesInRange:(NSRange)enumerationRange options:(NSAttributedStringEnumerationOptions)opts usingBlock:(void (NS_NOESCAPE ^)(NSDictionary<NSAttributedStringKey, id> *attrs, NSRange range, BOOL *stop))block;
```
* NSMutableAttributedString
```
- (instancetype)initWithString:(NSString *)str;
- (instancetype)initWithString:(NSString *)str attributes:(nullable NSDictionary<NSAttributedStringKey, id> *)attrs;
- (void)addAttribute:(NSAttributedStringKey)name value:(id)value range:(NSRange)range;
- (void)addAttributes:(NSDictionary<NSAttributedStringKey, id> *)attrs range:(NSRange)range;
- (void)removeAttribute:(NSAttributedStringKey)name range:(NSRange)range;
- (void)replaceCharactersInRange:(NSRange)range withAttributedString:(NSAttributedString *)attrString;
- (void)deleteCharactersInRange:(NSRange)range;
- (void)setAttributedString:(NSAttributedString *)attrString;
```
* NSArray * NSArray
``` ```
...@@ -82,3 +105,15 @@ ...@@ -82,3 +105,15 @@
- (void) addObject:(id)object; - (void) addObject:(id)object;
- (void) removeObject:(id)object; - (void) removeObject:(id)object;
``` ```
* NSTimer
```
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
```
* NSNotificationCenter
```
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject;
```
...@@ -199,6 +199,10 @@ KVO在以下情况会导致闪退: ...@@ -199,6 +199,10 @@ KVO在以下情况会导致闪退:
所以在没找到更好的办法,只能Swizzle dealloc方法,先清理kvo数据,再执行origin dealloc,不过这样就有个细节做不到,无法提示那些keyPath忘记清理. 所以在没找到更好的办法,只能Swizzle dealloc方法,先清理kvo数据,再执行origin dealloc,不过这样就有个细节做不到,无法提示那些keyPath忘记清理.
__2018/11/25__
Swizzle dealloc影响面相对偏广,后续找了一种只针对需要KVO需求类的Swizzle,当前类用AssociatedObject来记住状态,如果Swizzle过就直接返回,另外还加强KVO的健壮性,监听了observer状态,在宿主还没释放,observer先释放,需要要对observer对应的keyPath对应的清理.
## NSTimer ## NSTimer
NSTimer存在以下问题: NSTimer存在以下问题:
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment