Skip to content

Commit bf07b9c

Browse files
author
Mike Roberts
committed
Fixed memory management issues.
1 parent 8ecf137 commit bf07b9c

File tree

10 files changed

+291
-84
lines changed

10 files changed

+291
-84
lines changed

objc-promise.xcodeproj/project.pbxproj

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,17 @@
99
/* Begin PBXBuildFile section */
1010
78C4444B1628E0F00001DA25 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C4444A1628E0F00001DA25 /* Cocoa.framework */; };
1111
78C444551628E0F00001DA25 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 78C444531628E0F00001DA25 /* InfoPlist.strings */; };
12-
78C444591628E0F00001DA25 /* objc_promise.m in Sources */ = {isa = PBXBuildFile; fileRef = 78C444581628E0F00001DA25 /* objc_promise.m */; };
1312
78C444611628E0F00001DA25 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C444601628E0F00001DA25 /* SenTestingKit.framework */; };
1413
78C444621628E0F00001DA25 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C4444A1628E0F00001DA25 /* Cocoa.framework */; };
1514
78C444651628E0F00001DA25 /* objc-promise.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C444471628E0F00001DA25 /* objc-promise.framework */; };
1615
78C4446B1628E0F00001DA25 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 78C444691628E0F00001DA25 /* InfoPlist.strings */; };
17-
78C4446E1628E0F00001DA25 /* objc_promiseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 78C4446D1628E0F00001DA25 /* objc_promiseTests.m */; };
16+
78C444791628E2E30001DA25 /* Promise.h in Headers */ = {isa = PBXBuildFile; fileRef = 78C444771628E2E30001DA25 /* Promise.h */; };
17+
78C4447A1628E2E30001DA25 /* Promise.m in Sources */ = {isa = PBXBuildFile; fileRef = 78C444781628E2E30001DA25 /* Promise.m */; };
18+
78C4447D16291DD80001DA25 /* Deferred.h in Headers */ = {isa = PBXBuildFile; fileRef = 78C4447B16291DD80001DA25 /* Deferred.h */; };
19+
78C4447E16291DD80001DA25 /* Deferred.m in Sources */ = {isa = PBXBuildFile; fileRef = 78C4447C16291DD80001DA25 /* Deferred.m */; };
20+
78C44481162922B50001DA25 /* BasicPromiseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 78C44480162922B50001DA25 /* BasicPromiseTests.m */; };
21+
78C444821629FD870001DA25 /* Deferred.m in Sources */ = {isa = PBXBuildFile; fileRef = 78C4447C16291DD80001DA25 /* Deferred.m */; };
22+
78C444831629FD8B0001DA25 /* Promise.m in Sources */ = {isa = PBXBuildFile; fileRef = 78C444781628E2E30001DA25 /* Promise.m */; };
1823
/* End PBXBuildFile section */
1924

2025
/* Begin PBXContainerItemProxy section */
@@ -36,14 +41,16 @@
3641
78C444521628E0F00001DA25 /* objc-promise-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "objc-promise-Info.plist"; sourceTree = "<group>"; };
3742
78C444541628E0F00001DA25 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
3843
78C444561628E0F00001DA25 /* objc-promise-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "objc-promise-Prefix.pch"; sourceTree = "<group>"; };
39-
78C444571628E0F00001DA25 /* objc_promise.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = objc_promise.h; sourceTree = "<group>"; };
40-
78C444581628E0F00001DA25 /* objc_promise.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = objc_promise.m; sourceTree = "<group>"; };
4144
78C4445F1628E0F00001DA25 /* objc-promiseTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "objc-promiseTests.octest"; sourceTree = BUILT_PRODUCTS_DIR; };
4245
78C444601628E0F00001DA25 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; };
4346
78C444681628E0F00001DA25 /* objc-promiseTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "objc-promiseTests-Info.plist"; sourceTree = "<group>"; };
4447
78C4446A1628E0F00001DA25 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
45-
78C4446C1628E0F00001DA25 /* objc_promiseTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = objc_promiseTests.h; sourceTree = "<group>"; };
46-
78C4446D1628E0F00001DA25 /* objc_promiseTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = objc_promiseTests.m; sourceTree = "<group>"; };
48+
78C444771628E2E30001DA25 /* Promise.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Promise.h; sourceTree = "<group>"; };
49+
78C444781628E2E30001DA25 /* Promise.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Promise.m; sourceTree = "<group>"; };
50+
78C4447B16291DD80001DA25 /* Deferred.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Deferred.h; sourceTree = "<group>"; };
51+
78C4447C16291DD80001DA25 /* Deferred.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Deferred.m; sourceTree = "<group>"; };
52+
78C4447F162922B50001DA25 /* BasicPromiseTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BasicPromiseTests.h; sourceTree = "<group>"; };
53+
78C44480162922B50001DA25 /* BasicPromiseTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BasicPromiseTests.m; sourceTree = "<group>"; };
4754
/* End PBXFileReference section */
4855

4956
/* Begin PBXFrameworksBuildPhase section */
@@ -110,9 +117,11 @@
110117
78C444501628E0F00001DA25 /* objc-promise */ = {
111118
isa = PBXGroup;
112119
children = (
113-
78C444571628E0F00001DA25 /* objc_promise.h */,
114-
78C444581628E0F00001DA25 /* objc_promise.m */,
115120
78C444511628E0F00001DA25 /* Supporting Files */,
121+
78C444771628E2E30001DA25 /* Promise.h */,
122+
78C444781628E2E30001DA25 /* Promise.m */,
123+
78C4447B16291DD80001DA25 /* Deferred.h */,
124+
78C4447C16291DD80001DA25 /* Deferred.m */,
116125
);
117126
path = "objc-promise";
118127
sourceTree = "<group>";
@@ -130,9 +139,9 @@
130139
78C444661628E0F00001DA25 /* objc-promiseTests */ = {
131140
isa = PBXGroup;
132141
children = (
133-
78C4446C1628E0F00001DA25 /* objc_promiseTests.h */,
134-
78C4446D1628E0F00001DA25 /* objc_promiseTests.m */,
135142
78C444671628E0F00001DA25 /* Supporting Files */,
143+
78C4447F162922B50001DA25 /* BasicPromiseTests.h */,
144+
78C44480162922B50001DA25 /* BasicPromiseTests.m */,
136145
);
137146
path = "objc-promiseTests";
138147
sourceTree = "<group>";
@@ -153,6 +162,8 @@
153162
isa = PBXHeadersBuildPhase;
154163
buildActionMask = 2147483647;
155164
files = (
165+
78C444791628E2E30001DA25 /* Promise.h in Headers */,
166+
78C4447D16291DD80001DA25 /* Deferred.h in Headers */,
156167
);
157168
runOnlyForDeploymentPostprocessing = 0;
158169
};
@@ -263,15 +274,18 @@
263274
isa = PBXSourcesBuildPhase;
264275
buildActionMask = 2147483647;
265276
files = (
266-
78C444591628E0F00001DA25 /* objc_promise.m in Sources */,
277+
78C4447A1628E2E30001DA25 /* Promise.m in Sources */,
278+
78C4447E16291DD80001DA25 /* Deferred.m in Sources */,
267279
);
268280
runOnlyForDeploymentPostprocessing = 0;
269281
};
270282
78C4445A1628E0F00001DA25 /* Sources */ = {
271283
isa = PBXSourcesBuildPhase;
272284
buildActionMask = 2147483647;
273285
files = (
274-
78C4446E1628E0F00001DA25 /* objc_promiseTests.m in Sources */,
286+
78C44481162922B50001DA25 /* BasicPromiseTests.m in Sources */,
287+
78C444821629FD870001DA25 /* Deferred.m in Sources */,
288+
78C444831629FD8B0001DA25 /* Promise.m in Sources */,
275289
);
276290
runOnlyForDeploymentPostprocessing = 0;
277291
};

objc-promise/Deferred.m

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

99
#import "Deferred.h"
1010

11+
typedef void (^bound_block)(void);
12+
13+
@implementation Deferred (Private)
14+
15+
- (void)transitionToState:(PromiseState)state
16+
{
17+
NSArray *blocksToExecute = nil;
18+
BOOL shouldComplete = NO;
19+
20+
@synchronized (_stateLock) {
21+
if (_state == Incomplete) {
22+
_state = state;
23+
24+
shouldComplete = YES;
25+
26+
blocksToExecute = [_callbackBindings retain];
27+
28+
[_callbackBindings release];
29+
_callbackBindings = nil;
30+
}
31+
}
32+
33+
if (shouldComplete) {
34+
for (bound_block block in blocksToExecute) {
35+
block();
36+
37+
Block_release(block);
38+
}
39+
}
40+
41+
[blocksToExecute release];
42+
}
43+
44+
@end
45+
1146
@implementation Deferred
1247

48+
- (id)init
49+
{
50+
if (self = [super init]) {
51+
}
52+
53+
return self;
54+
}
55+
56+
+ (Deferred *)deferred
57+
{
58+
return [[[Deferred alloc] init] autorelease];
59+
}
60+
61+
- (Promise *)promise
62+
{
63+
return self;
64+
}
65+
66+
- (Promise *)resolve:(id)result
67+
{
68+
_result = [result retain];
69+
70+
[self transitionToState:Resolved];
71+
72+
return [self promise];
73+
}
74+
75+
- (Promise *)reject:(NSError *)reason
76+
{
77+
_reason = [reason retain];
78+
79+
[self transitionToState:Rejected];
80+
81+
return [self promise];
82+
}
83+
1384
@end

objc-promise/Promise.h

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,47 @@
88

99
#import <Cocoa/Cocoa.h>
1010

11-
@interface Promise : NSObject
11+
@class Promise;
12+
13+
typedef void (^resolved_block)(id);
14+
typedef void (^rejected_block)(NSError *);
15+
typedef void (^any_block)(void);
16+
17+
typedef Promise *(^then_block)(resolved_block);
18+
typedef Promise *(^failed_block)(rejected_block);
19+
typedef Promise *(^done_block)(any_block);
20+
21+
typedef Promise *(^then_on_block)(resolved_block);
22+
typedef Promise *(^failed_on_block)(rejected_block);
23+
typedef Promise *(^done_on_block)(any_block);
24+
25+
typedef enum {
26+
Incomplete = 0,
27+
Rejected = 1,
28+
Resolved = 2
29+
} PromiseState;
30+
31+
@interface Promise : NSObject {
32+
NSMutableArray *_callbackBindings;
33+
34+
NSObject *_stateLock;
35+
PromiseState _state;
36+
37+
id _result;
38+
NSError *_reason;
39+
40+
then_block _then;
41+
failed_block _failed;
42+
done_block _done;
43+
}
44+
45+
@property (readonly) id result;
46+
@property (readonly) NSError *reason;
47+
@property (readonly) BOOL isResolved;
48+
@property (readonly) BOOL isRejected;
49+
50+
@property (readonly) then_block then;
51+
@property (readonly) failed_block failed;
52+
@property (readonly) done_block done;
1253

1354
@end

objc-promise/Promise.m

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

99
#import "Promise.h"
1010

11+
typedef void (^bound_block)(void);
12+
13+
@implementation Promise (Private)
14+
15+
- (BOOL)bindOrCallBlock:(bound_block)block
16+
{
17+
BOOL blockWasBound = NO;
18+
19+
@synchronized (_stateLock) {
20+
if (_state == Incomplete) {
21+
[_callbackBindings addObject:Block_copy(block)];
22+
23+
blockWasBound = YES;
24+
}
25+
}
26+
27+
if (!blockWasBound) {
28+
block();
29+
}
30+
31+
return blockWasBound;
32+
}
33+
34+
@end
35+
1136
@implementation Promise
1237

38+
@synthesize then = _then, failed = _failed, done = _done;
39+
@synthesize result = _result, reason = _reason;
40+
@dynamic isResolved, isRejected;
41+
42+
- (id)init
43+
{
44+
if (self = [super init]) {
45+
// bind self reference to a block variable to ensure
46+
// self is not retained by the block methods it owns
47+
__block Promise *this = self;
48+
49+
_callbackBindings = [[NSMutableArray alloc] init];
50+
_state = Incomplete;
51+
52+
_stateLock = [[NSObject alloc] init];
53+
_result = nil;
54+
55+
_then = Block_copy(^Promise *(resolved_block resolvedBlock){
56+
// retain the block until we can call with the result
57+
Block_copy(resolvedBlock);
58+
59+
[this bindOrCallBlock:^{
60+
if (this.isResolved) {
61+
resolvedBlock(this.result);
62+
}
63+
64+
Block_release(resolvedBlock);
65+
}];
66+
67+
return this;
68+
});
69+
70+
_failed = Block_copy(^Promise *(rejected_block rejectedBlock){
71+
// retain the block until we can call with the result
72+
Block_copy(rejectedBlock);
73+
74+
[this bindOrCallBlock:^{
75+
if (this.isRejected) {
76+
rejectedBlock(this.reason);
77+
}
78+
79+
Block_release(rejectedBlock);
80+
}];
81+
82+
return this;
83+
});
84+
85+
_done = Block_copy(^Promise *(any_block anyBlock){
86+
// retain the block until we can call with the result
87+
Block_copy(anyBlock);
88+
89+
[this bindOrCallBlock:^{
90+
anyBlock();
91+
92+
Block_release(anyBlock);
93+
}];
94+
95+
return this;
96+
});
97+
}
98+
99+
return self;
100+
}
101+
102+
- (void)dealloc
103+
{
104+
[_callbackBindings release];
105+
_callbackBindings = nil;
106+
107+
[_stateLock release];
108+
_stateLock = nil;
109+
110+
[_result release];
111+
_result = nil;
112+
113+
[_reason release];
114+
_reason = nil;
115+
116+
Block_release(_then);
117+
Block_release(_failed);
118+
Block_release(_done);
119+
120+
[super dealloc];
121+
}
122+
123+
- (BOOL)isResolved
124+
{
125+
return _state == Resolved;
126+
}
127+
128+
- (BOOL)isRejected
129+
{
130+
return _state == Rejected;
131+
}
132+
13133
@end

objc-promise/objc_promise.h

Lines changed: 0 additions & 13 deletions
This file was deleted.

objc-promise/objc_promise.m

Lines changed: 0 additions & 13 deletions
This file was deleted.

objc-promiseTests/BasicPromiseTests.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
//
88

99
#import <SenTestingKit/SenTestingKit.h>
10+
#import "Promise.h"
11+
#import "Deferred.h"
1012

1113
@interface BasicPromiseTests : SenTestCase
1214

0 commit comments

Comments
 (0)