Skip to content

Commit a0f3ad7

Browse files
author
李杰
committed
Refactor the KVO
1 parent 0c4cca0 commit a0f3ad7

File tree

9 files changed

+234
-66
lines changed

9 files changed

+234
-66
lines changed

JJException.podspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Pod::Spec.new do |s|
1616
#
1717

1818
s.name = "JJException"
19-
s.version = "0.1.7"
19+
s.version = "0.1.8"
2020
s.summary = "Handle the objective-c crash exception."
2121

2222
# This description is used to generate tags and improve search results.
@@ -69,7 +69,7 @@ Pod::Spec.new do |s|
6969

7070
# When using multiple platforms
7171
s.ios.deployment_target = "8.0"
72-
s.osx.deployment_target = "10.7"
72+
s.osx.deployment_target = "10.8"
7373
s.watchos.deployment_target = "2.0"
7474
s.tvos.deployment_target = "9.0"
7575

JJException.xcodeproj/xcuserdata/jezz.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist

Lines changed: 5 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -27,61 +27,13 @@
2727
shouldBeEnabled = "Yes"
2828
ignoreCount = "0"
2929
continueAfterRunningActions = "No"
30-
filePath = "JJException/Source/MRC/NSObject+KVOCrash.m"
31-
timestampString = "564488427.585709"
30+
filePath = "JJException/Source/DeallocBlock/NSObject+DeallocBlock.m"
31+
timestampString = "564822196.511668"
3232
startingColumnNumber = "9223372036854775807"
3333
endingColumnNumber = "9223372036854775807"
34-
startingLineNumber = "133"
35-
endingLineNumber = "133"
36-
landmarkName = "-clearKVOData"
37-
landmarkType = "7">
38-
<Locations>
39-
<Location
40-
shouldBeEnabled = "Yes"
41-
ignoreCount = "0"
42-
continueAfterRunningActions = "No"
43-
symbolName = "-[KVOObjectContainer clearKVOData]"
44-
moduleName = "JJException"
45-
usesParentBreakpointCondition = "Yes"
46-
urlString = "file:///Users/jezz/git_project/JJExceptionGithub/JJException/Source/MRC/NSObject+KVOCrash.m"
47-
timestampString = "564488398.348398"
48-
startingColumnNumber = "9223372036854775807"
49-
endingColumnNumber = "9223372036854775807"
50-
startingLineNumber = "133"
51-
endingLineNumber = "133"
52-
offsetFromSymbolStart = "55">
53-
</Location>
54-
<Location
55-
shouldBeEnabled = "Yes"
56-
ignoreCount = "0"
57-
continueAfterRunningActions = "No"
58-
symbolName = "-[KVOObjectContainer clearKVOData]"
59-
moduleName = "JJException"
60-
usesParentBreakpointCondition = "Yes"
61-
urlString = "file:///Users/jezz/git_project/JJExceptionGithub/JJException/Source/MRC/NSObject+KVOCrash.m"
62-
timestampString = "564488398.350692"
63-
startingColumnNumber = "9223372036854775807"
64-
endingColumnNumber = "9223372036854775807"
65-
startingLineNumber = "133"
66-
endingLineNumber = "133"
67-
offsetFromSymbolStart = "65">
68-
</Location>
69-
</Locations>
70-
</BreakpointContent>
71-
</BreakpointProxy>
72-
<BreakpointProxy
73-
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
74-
<BreakpointContent
75-
shouldBeEnabled = "Yes"
76-
ignoreCount = "0"
77-
continueAfterRunningActions = "No"
78-
filePath = "JJException/Source/MRC/NSObject+KVOCrash.m"
79-
timestampString = "564488427.587878"
80-
startingColumnNumber = "9223372036854775807"
81-
endingColumnNumber = "9223372036854775807"
82-
startingLineNumber = "244"
83-
endingLineNumber = "244"
84-
landmarkName = "-kvo_hookDealloc"
34+
startingLineNumber = "26"
35+
endingLineNumber = "26"
36+
landmarkName = "-dealloc"
8537
landmarkType = "7">
8638
</BreakpointContent>
8739
</BreakpointProxy>

JJException/PushViewController.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ - (void)viewDidLoad {
5656

5757
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
5858
_kvoObserver = nil;
59+
self.demoString1 = @"11";
5960
});
6061

6162
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

JJException/Source/MRC/NSObject+KVOCrash.m

Lines changed: 77 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#import "JJExceptionProxy.h"
1414

1515
static const char DeallocKVOKey;
16+
static const char ObserverDeallocKVOKey;
1617

1718
/**
1819
Record the kvo object
@@ -51,6 +52,7 @@ - (void)dealloc{
5152

5253
@end
5354

55+
5456
@interface KVOObjectContainer : NSObject
5557

5658
/**
@@ -129,7 +131,7 @@ - (void)dealloc{
129131
[super dealloc];
130132
}
131133

132-
- (void)clearKVOData{
134+
- (void)cleanKVOData{
133135
for (KVOObjectItem* item in self.kvoObjectSet) {
134136
#pragma clang diagnostic push
135137
#pragma clang diagnostic ignored "-Wundeclared-selector"
@@ -151,14 +153,66 @@ - (NSMutableSet*)kvoObjectSet{
151153

152154
@end
153155

156+
@interface JJObserverContainer : NSObject
157+
158+
@property (nonatomic,readwrite,strong) NSHashTable* observers;
159+
160+
/**
161+
Associated owner object
162+
*/
163+
@property(nonatomic,readwrite,unsafe_unretained)NSObject* whichObject;
164+
165+
- (void)addObserver:(KVOObjectItem *)observer;
166+
167+
- (void)removeObserver:(KVOObjectItem *)observer;
168+
169+
@end
170+
171+
@implementation JJObserverContainer
172+
173+
- (instancetype)init
174+
{
175+
self = [super init];
176+
if (self) {
177+
self.observers = [NSHashTable hashTableWithOptions:NSMapTableWeakMemory];
178+
}
179+
return self;
180+
}
181+
182+
- (void)addObserver:(KVOObjectItem *)observer
183+
{
184+
@synchronized (self) {
185+
[self.observers addObject:observer];
186+
}
187+
}
188+
189+
- (void)removeObserver:(KVOObjectItem *)observer
190+
{
191+
@synchronized (self) {
192+
[self.observers removeObject:observer];
193+
}
194+
}
195+
196+
- (void)cleanObservers{
197+
for (KVOObjectItem* item in self.observers) {
198+
[self.whichObject removeObserver:item.observer forKeyPath:item.keyPath];
199+
}
200+
}
201+
202+
- (void)dealloc{
203+
self.whichObject = nil;
204+
[self.observers release];
205+
[super dealloc];
206+
}
207+
208+
@end
209+
154210
@implementation NSObject (KVOCrash)
155211

156212
+ (void)jj_swizzleKVOCrash{
157213
swizzleInstanceMethod([self class], @selector(addObserver:forKeyPath:options:context:), @selector(hookAddObserver:forKeyPath:options:context:));
158214
swizzleInstanceMethod([self class], @selector(removeObserver:forKeyPath:), @selector(hookRemoveObserver:forKeyPath:));
159215
swizzleInstanceMethod([self class], @selector(removeObserver:forKeyPath:context:), @selector(hookRemoveObserver:forKeyPath:context:));
160-
//Swizzle kvo dealloc
161-
swizzleInstanceMethod([self class], @selector(dealloc), @selector(kvo_hookDealloc));
162216
}
163217

164218
- (void)hookAddObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{
@@ -191,7 +245,22 @@ - (void)hookAddObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath opti
191245
[self hookAddObserver:observer forKeyPath:keyPath options:options context:context];
192246
}
193247

248+
JJObserverContainer* observerContainer = objc_getAssociatedObject(observer,&ObserverDeallocKVOKey);
249+
250+
if (!observerContainer) {
251+
observerContainer = [JJObserverContainer new];
252+
[observerContainer setWhichObject:self];
253+
[observerContainer addObserver:item];
254+
objc_setAssociatedObject(observer, &ObserverDeallocKVOKey, observerContainer, OBJC_ASSOCIATION_RETAIN);
255+
[observerContainer release];
256+
}else{
257+
[observerContainer addObserver:item];
258+
}
259+
194260
[item release];
261+
262+
jj_swizzleDeallocIfNeeded(self.class);
263+
jj_swizzleDeallocIfNeeded(observer.class);
195264
}
196265

197266
- (void)hookRemoveObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(void*)context{
@@ -236,19 +305,16 @@ - (void)hookRemoveObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath{
236305

237306

238307
/**
239-
* Hook the kvo object dealloc and to clean the kvo array,
240-
* And show the more kvo object info to the user
308+
* Hook the kvo object dealloc and to clean the kvo array
241309
*/
242-
- (void)kvo_hookDealloc{
310+
- (void)jj_cleanKVO{
243311

244312
KVOObjectContainer* objectContainer = objc_getAssociatedObject(self, &DeallocKVOKey);
313+
[objectContainer cleanKVOData];
245314

246-
if (objectContainer) {
247-
[objectContainer clearKVOData];
248-
}
315+
JJObserverContainer* observerContainer = objc_getAssociatedObject(self, &ObserverDeallocKVOKey);
316+
[observerContainer cleanObservers];
249317

250-
//Invoke the origin dealloc
251-
[self kvo_hookDealloc];
252318
}
253319

254320
@end

JJException/Source/Swizzle/NSObject+SwizzleHook.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88

99
#import <Foundation/Foundation.h>
1010

11+
12+
/*
13+
* JJSwizzledIMPBlock assist variable
14+
*/
15+
1116
typedef void (*JJSwizzleOriginalIMP)(void /* id, SEL, ... */ );
1217

1318
@interface JJSwizzleObject : NSObject
@@ -20,16 +25,64 @@ typedef void (*JJSwizzleOriginalIMP)(void /* id, SEL, ... */ );
2025

2126
typedef id (^JJSwizzledIMPBlock)(JJSwizzleObject* swizzleInfo);
2227

28+
/*
29+
* JJSwizzledIMPBlock assist variable
30+
*/
31+
32+
33+
/**
34+
* Swizzle Class Method
35+
36+
@param cls Class
37+
@param originSelector originSelector
38+
@param swizzleSelector swizzleSelector
39+
*/
2340
void swizzleClassMethod(Class cls, SEL originSelector, SEL swizzleSelector);
2441

42+
/**
43+
* Swizzle Instance Class Method
44+
45+
@param cls Class
46+
@param originSelector originSelector
47+
@param swizzleSelector swizzleSelector
48+
*/
2549
void swizzleInstanceMethod(Class cls, SEL originSelector, SEL swizzleSelector);
2650

51+
/**
52+
* Only swizzle the current class,not swizzle all class
53+
* perform jj_cleanKVO selector before the origin dealloc
54+
55+
@param class Class
56+
*/
57+
void jj_swizzleDeallocIfNeeded(Class class);
58+
59+
/**
60+
Swizzle the NSObject Extension
61+
*/
2762
@interface NSObject (SwizzleHook)
2863

64+
/**
65+
Swizzle Class Method
66+
67+
@param originSelector originSelector
68+
@param swizzleSelector swizzleSelector
69+
*/
2970
+ (void)jj_swizzleClassMethod:(SEL)originSelector withSwizzleMethod:(SEL)swizzleSelector;
3071

72+
/**
73+
Swizzle Instance Method
74+
75+
@param originSelector originSelector
76+
@param swizzleSelector swizzleSelector
77+
*/
3178
- (void)jj_swizzleInstanceMethod:(SEL)originSelector withSwizzleMethod:(SEL)swizzleSelector;
3279

80+
/**
81+
Swizzle instance method to the block target
82+
83+
@param originSelector originSelector
84+
@param swizzledBlock block
85+
*/
3386
- (void)jj_swizzleInstanceMethod:(SEL)originSelector withSwizzledBlock:(JJSwizzledIMPBlock)swizzledBlock;
3487

3588
@end

JJException/Source/Swizzle/NSObject+SwizzleHook.m

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@
88

99
#import "NSObject+SwizzleHook.h"
1010
#import <objc/runtime.h>
11+
#import <objc/message.h>
1112
#import <libkern/OSAtomic.h>
1213

1314
typedef IMP (^JJSWizzleImpProvider)(void);
1415

16+
static const char jjSwizzledDeallocKey;
17+
1518
@interface JJSwizzleObject()
1619
@property (nonatomic,readwrite,copy) JJSWizzleImpProvider impProviderBlock;
1720
@property (nonatomic,readwrite,assign) SEL selector;
@@ -88,6 +91,60 @@ void swizzleInstanceMethod(Class cls, SEL originSelector, SEL swizzleSelector){
8891
}
8992
}
9093

94+
// a class doesn't need dealloc swizzled if it or a superclass has been swizzled already
95+
BOOL jj_requiresDeallocSwizzle(Class class)
96+
{
97+
BOOL swizzled = NO;
98+
99+
for ( Class currentClass = class; !swizzled && currentClass != nil; currentClass = class_getSuperclass(currentClass) ) {
100+
swizzled = [objc_getAssociatedObject(currentClass, &jjSwizzledDeallocKey) boolValue];
101+
}
102+
103+
return !swizzled;
104+
}
105+
106+
void jj_swizzleDeallocIfNeeded(Class class)
107+
{
108+
static SEL deallocSEL = NULL;
109+
static SEL cleanupSEL = NULL;
110+
111+
static dispatch_once_t onceToken;
112+
dispatch_once(&onceToken, ^{
113+
deallocSEL = sel_getUid("dealloc");
114+
cleanupSEL = sel_getUid("jj_cleanKVO");
115+
});
116+
117+
@synchronized (class) {
118+
if ( !jj_requiresDeallocSwizzle(class) ) {
119+
return;
120+
}
121+
122+
objc_setAssociatedObject(class, &jjSwizzledDeallocKey, @(YES), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
123+
}
124+
125+
Method dealloc = class_getInstanceMethod(class, deallocSEL);
126+
127+
if ( dealloc == NULL ) {
128+
Class superclass = class_getSuperclass(class);
129+
130+
class_addMethod(class, deallocSEL, imp_implementationWithBlock(^(__unsafe_unretained id self) {
131+
132+
((void(*)(id, SEL))objc_msgSend)(self, cleanupSEL);
133+
134+
struct objc_super superStruct = (struct objc_super){ self, superclass };
135+
((void (*)(struct objc_super*, SEL))objc_msgSendSuper)(&superStruct, deallocSEL);
136+
137+
}), method_getTypeEncoding(dealloc));
138+
}else{
139+
__block IMP deallocIMP = method_setImplementation(dealloc, imp_implementationWithBlock(^(__unsafe_unretained id self) {
140+
((void(*)(id, SEL))objc_msgSend)(self, cleanupSEL);
141+
142+
((void(*)(id, SEL))deallocIMP)(self, deallocSEL);
143+
}));
144+
}
145+
}
146+
147+
91148
@implementation NSObject (SwizzleHook)
92149

93150
void __JJ_SWIZZLE_BLOCK(Class classToSwizzle,SEL selector,JJSwizzledIMPBlock impBlock){

0 commit comments

Comments
 (0)