From ActionScript to Objective-C: Part II

I will cover the creation of LinePath2D and PathPoint first, showing how to turn them from ActionScript to Objective-C and along the way I will cover all the main points and peculiarities of that language.

Let me start with PathPoint.

Here it is in ActionScript:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
 
package  {
 
 
 
	import flash.geom.Matrix;
 
	import flash.geom.Point;
 
	public class LinePath2D {		
 
		private static const _RAD2DEG:Number = 180 / Math.PI;
 
		private static const _DEG2RAD:Number = Math.PI / 180;
 
		public var angle:Number;
 
		private var _first:PathPoint;
 
		private var _points:Array;
 
		private var _totalLength:Number;
 
 
 
		public function LinePath2D() {
 
			super();
 
			_points = [];
 
			_totalLength = 0;
 
		}
 
 
 
		public function appendPoint(point:Point):void {
 
			_insertPoint(point, _points.length, false);
 
		}
 
 
 
		private function _insertPoint(point:Point, index:uint, skipOrganize:Boolean):void {
 
			_points.splice(index, 0, new PathPoint(point));
 
			if (!skipOrganize) {
 
				_organize();
 
			}
 
		}
 
 
 
		public function insertMultiplePoints(points:Array, index:uint=0):void {
 
			var l:int = points.length;
 
			for (var i:int = 0; i < l; i++) {
 
				_insertPoint(points[i], index + i, true);
 
			}
 
		}
 
 
 
		private function _organize():void {
 
			_totalLength = 0;
 
			var last:int = _points.length - 1;
 
			if (last == -1) {
 
				_first = null;
 
			} else if (last == 0) {
 
				_first = _points[0];
 
				_first.progress = _first.xChange = _first.yChange = _first.length = 0;
 
				return;
 
			}
 
			var pp:PathPoint;
 
			for (var i:int = 0; i <= last; i++) { 
 
				if (_points[i] != null) {
 
					pp = _points[i];
 
					pp.x = pp.point.x;
 
					pp.y = pp.point.y;
 
					if (i == last) {
 
						pp.length = 0;
 
						pp.next = null;
 
					} else {
 
						pp.next = _points[i + 1];
 
						pp.xChange = pp.next.x - pp.x;
 
						pp.yChange = pp.next.y - pp.y;
 
						pp.length = Math.sqrt(pp.xChange * pp.xChange + pp.yChange * pp.yChange);
 
						_totalLength += pp.length;
 
					}
 
				}
 
			}
 
			_first = pp = _points[0];
 
			var curTotal:Number = 0;
 
			while (pp) {
 
				pp.progress = curTotal / _totalLength;
 
				curTotal += pp.length;
 
				pp = pp.next;
 
			}
 
			_updateAngles();
 
		}
 
 
 
		private function _updateAngles():void {
 
			var pp:PathPoint = _first;
 
			while (pp) {
 
				pp.angle = Math.atan2(pp.yChange, pp.xChange ) * _RAD2DEG;
 
				pp = pp.next;
 
			}
 
 
 
		}
 
 
 
		public function getPointAtProgress (progress:Number):Point {
 
			progress = Math.abs(progress);
 
 
 
			var point:Point = new Point();
 
			if (progress > 1) progress = 1;
 
			var pp:PathPoint = _first;
 
			if (!pp.next) return null;
 
			if (!pp.next.progress) return null;
 
 
 
			while (pp.next != null && pp.next.progress < progress) {
 
				pp = pp.next;
 
			}
 
 
 
			if (pp != null) {
 
				angle = pp.angle;
 
				var pathProg:Number = (progress - pp.progress) / (pp.length / _totalLength);
 
				point.x = pp.x + pathProg * pp.xChange;
 
				point.y = pp.y + pathProg * pp.yChange;
 
 
 
				return point;
 
 
 
			}
 
			return null;
 
		}
 
 
 
		public function renderObjectAt(target:Object, progress:Number, autoRotate:Boolean=false, rotationOffset:Number=0):void {
 
			if (progress > 1) {
 
				progress -= int(progress);
 
			} else if (progress < 0) {
 
				progress -= int(progress) - 1;
 
			}
 
			if (_first == null) {
 
				return;
 
			}
 
 
 
			var pp:PathPoint = _first;
 
			while (pp.next != null && pp.next.progress < progress) {
 
				pp = pp.next;
 
			}
 
 
 
			if (pp != null) {
 
				angle = pp.angle;
 
				var pathProg:Number = (progress - pp.progress) / (pp.length / _totalLength);
 
				var px:Number = pp.x + pathProg * pp.xChange;
 
				var py:Number = pp.y + pathProg * pp.yChange;
 
 
 
				target.x = px ;
 
				target.y = py ;
 
 
 
			}
 
 
 
		}
 
 
 
		public function get totalLength():Number {
 
			return _totalLength;
 
		}
 
 
 
		public function get points():Array {
 
			return _points;
 
		}
 
		public function set points(value:Array):void {
 
			_points = [];
 
			insertMultiplePoints(value, 0);
 
		}
 
 
 
 
 
	}
 
}

This is an object used in the Greensock api to store the points that form the path the plane will follow along later. It only stores data, it doesn’t do anything with it.

The H and the M thing

Most Objective-C classes will have its properties and methods listed in an H file, confusingly called an interface, and then the methods are described in an M file confusingly called the implementation. The closest thing to the Actionscript interface in Objective-C is something called a protocol, but the H file is close enough too, so screw it, let’s call it an interface.

In Objective-C the interface for PathPoint looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
 
#import <Foundation/Foundation.h>
 
@class CCSprite;
 
@class PathPoint;
 
@interface LinePath2D : NSObject {
 
 
 
	float _angle;
 
	NSMutableArray *_points;
 
	float _totalLength;
 
	PathPoint *_first;
 
}
 
@property float angle;
 
@property float totalLength;
 
@property (nonatomic, assign) NSMutableArray *points;
 
-(void) appendPoint:(CGPoint) point;
 
-(void) insertPoint:(CGPoint) point atIndex:(NSInteger)index andSkipOrganize:(BOOL)skipOrganize;
 
-(void) insertMultiplePoints:(NSMutableArray *) points atIndex:(NSInteger) index;
 
-(void) organize;
 
-(void) updateAngles;
 
-(NSValue *) getPointAtProgress:(float) progress;
 
-(void) renderObject:(CCSprite *) object at:(float) progress;
 
@end

We import Foundation because this is where NSObject lives and a bunch of other great stuff. The class extends NSObject. And you must explicitly extend something. In ActionScript if your class does not extend anything, it extends Object. In Objective-C that would be the equivalent of extending NSObject, only you must say it so.

The * thing

In Objetive-C you must allocate memory for every new object, but more on memory management later. The asterisk is called a pointer, and it’s used mainly for objects, although you can use them for primitive types. There are many important differences between Objects and Primitive types and how they are treated. I will point these out as they appear.

Right now just notice that floats and points don’t usually get a pointer. They are primitive types. They are not allocated in the same way objects are and they are not released the way objects are.

The pointer in the PathPoint *next is saying that somewhere in your computer memory, a space was allocated to store an object of type PathPoint and the variable “next” holds the address/location for that bit of memory. So pointer variables POINTS to the location where data is stored for a particular object.

The Property thing

Next, you turn the variables you want to make public into properties.

What are the attributes thing? Properties will have Getters and Setters, and XCode can build them automatically for you when you later use the command SYNTHESIZE (I will show this in a minute.) So the attributes tell the compiler what to include in the Getter and Setter. Or how to make them. So if you say NonAtomic, when the compiler creates the the setter logic, it will include logic that turns the variable being set to nonatomic. Or if you use the attribute readonly, then the compiler doesn’t create a setter at all.

What is Atomicity? This has to do with multiple Threads. When a variable is said to be atomic it can only be written to if it’s not being read, and it can only be read if it’s not being written to. This protects your logic if your application uses many threads and more than one thread accesses the same variable. But it uses more memory and processing to keep things straight. You can also synchronize blocks of code just as you do with Java… But we don’t generally use multiple threads for 2D games and so make sure you use less processing and memory by setting object properties to nonatomic.

The attribute Assign works the way it says it, it makes the value of the property BE the value passed to the setter. Retain will make the property RETAIN the value passed, and so increase the retain count for that same memory address. Change the data in the original object and the change will take place at this retained reference as well. Copy, on the other hand, will create a copy of the value passed, then if the original object is changed the change won’t be reflected on the copy. Copy is useful for non atomic arrays and primitives, Assign is useful for Objects in general, and Retain is useful if the value passed is set to autorelease. But more on that later.

One other thing. You will see later that we use the command SYNTHESIZE to automatically create getters and setters for properties. But you can override these. Here are the main points to consider:

1
2
3
4
5
6
7
8
 
-(void) insertMultiplePoints:(NSMutableArray *) points atIndex:(NSInteger) index;
 
-(NSValue *) getPointAtProgress:(float) progress;
 
//In castings too
 
NSString *valueFrom = (NSString *) myObjectX;

Now for more the retain/copy attributes and auto release… Oh, boy. Keep reading…

The Memory thing

No Garbage Collector in Objective-C. That’s right. Nothing that will make much of an impression anyway. So in Objective-C you, the developer, allocate memory, and you clear that memory. You can play with your toys but only if you promise to put them away once you are done playing.

The closest thing to a Garbage Collector is the Auto Release Pool. This gets created with every application, and it’s basically an array with all objects you created and added a reference to this array. Then once at the end of every iteration (or Event Thread iteration to be precise) all objects listed in the array are destroyed. At the end of your application’s main thread the pool is destroyed.

Check out the main.m file which is the starting point of every Objective-C application.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
 
#import "LinePath2D.h"
 
#import "PathPoint.h"
 
#import "cocos2d.h"
 
#define RAD2DEG  180/M_PI;
 
@implementation LinePath2D
 
@synthesize totalLength = _totalLength, angle = _angle;
 
@synthesize points = _points;
 
-(id) init {
 
	self = [super init];
 
	if (self != nil) {
 
		_points = [[NSMutableArray array] retain];
 
		_totalLength = 0;
 
        _angle = 0;
 
	}
 
	return self;
 
}
 
-(void) appendPoint:(CGPoint) point {
 
	[self insertPoint:point atIndex:[_points count] andSkipOrganize:NO];
 
}
 
-(void) insertPoint:(CGPoint)point atIndex:(NSInteger)index andSkipOrganize:(BOOL)skipOrganize {
 
    if ([_points count] == 0) {
 
        [_points addObject:[PathPoint withPoint:point]];
 
    } else {
 
        [_points insertObject:[PathPoint withPoint:point] atIndex:index];
 
    }
 
	;
 
    //NSLog(@"Length: %i", [_points count]);
 
	if (!skipOrganize) {
 
		[self organize];
 
	}
 
}
 
-(void) insertMultiplePoints:(NSMutableArray *) points atIndex:(NSInteger) index {
 
 
 
	NSInteger len = [points count];
 
	for (int i = 0; i < len; i++) {
 
		NSValue *value = [points objectAtIndex:i];
 
		CGPoint point = [value CGPointValue];
 
		[self insertPoint:point atIndex:(index + i) andSkipOrganize:YES];
 
	}
 
}
 
-(void) organize {
 
	_totalLength = 0;
 
	NSInteger last = [_points count] - 1;
 
	if (last == -1) {
 
		_first = nil;
 
	} else if (last == 0) {
 
		_first = [_points objectAtIndex:0];
 
		_first.progress = _first.xChange = _first.yChange = _first.length = 0;
 
		return;
 
	}
 
	PathPoint *pp;
 
	for (int i = 0; i <= last; i++) {
 
		if ([_points objectAtIndex:i] != NULL) {
 
			pp = [_points objectAtIndex:i];
 
			pp.x = pp.point.x;
 
			pp.y = pp.point.y;
 
			if (i == last) {
 
				pp.length = 0;
 
				pp.next = NULL;
 
			} else {
 
				pp.next = [_points objectAtIndex: (i+1)];
 
				pp.xChange = pp.next.x - pp.x;
 
				pp.yChange = pp.next.y - pp.y;
 
				pp.length = sqrt(pow(pp.xChange, 2) + pow(pp.yChange, 2));
 
				_totalLength += pp.length;
 
			}
 
		}
 
	}
 
	_first = pp = [_points objectAtIndex:0];
 
	float curTotal = 0;
 
 
 
	while (pp != nil) {
 
		pp.progress = curTotal / _totalLength;
 
		curTotal += pp.length;
 
		pp = pp.next;
 
	}
 
 
 
	[self updateAngles];
 
 
 
}
 
-(void) updateAngles {
 
	PathPoint *pp = _first;
 
	while (pp) {
 
		pp.angle = -1 * atan2(pp.yChange, pp.xChange) * RAD2DEG;
 
        pp = pp.next;
 
 
 
	}
 
}
 
-(NSValue*) getPointAtProgress:(float) progress {
 
	progress = fabs(progress);
 
	if (progress > 1) progress = 1;
 
	PathPoint *pp = _first;
 
 
 
	if (pp.next == nil) return nil;
 
	if (!pp.next.progress) return nil;
 
	while (pp.next != nil && pp.next.progress < progress) { 
 
		pp = pp.next;
 
	}
 
	if (pp != nil) {
 
		_angle = pp.angle;
 
		float pathProg = ( progress - pp.progress) / (pp.length / _totalLength);
 
        return [NSValue valueWithCGPoint:CGPointMake(pp.x + pathProg * pp.xChange, pp.y + pathProg * pp.yChange)];
 
	}
 
	return nil;
 
 
 
}
 
-(void) renderObject:(CCSprite *)object at:(float) progress {
 
	if (progress > 1) {
 
		progress -= floor(progress);
 
	} else if (progress < 0) {
 
		progress -= floor(progress) - 1;
 
	}
 
	if (_first == nil) {
 
		return;
 
	}
 
	PathPoint *pp = _first;
 
	while (pp.next != nil && pp.next.progress < progress) {
 
		pp = pp.next;
 
	}
 
	if (pp != nil) {
 
		_angle = pp.angle;
 
 
 
		float pathProg =  (progress - pp.progress) / (pp.length / _totalLength);
 
 
 
        object.position = CGPointMake(pp.x + pathProg * pp.xChange, pp.y + pathProg * pp.yChange);		
 
	}
 
 
 
}
 
-(void) setPoints:(NSMutableArray *) points {
 
	[_points removeAllObjects];
 
	[self insertMultiplePoints:points atIndex:0];
 
}
 
-(void) dealloc {
 
 
 
	[_points release];
 
 
 
	[super dealloc];
 
}
 
@end

Pool is created, main thread is created, and once the thread is done, your code gets to the next line where it says pool release.

That’s right, your entire application runs in that one line. Your loops and hopes, all meshed together between create pool and release pool.

I will show you later how to set an object to be autoreleased (meaning, how to add it to the pool.)

Now for the meaty part.

When you don’t use the autorelease, it is up to you to clear your allocated memory whenever you use any of these keywords:

– alloc (without the added autorelease command)

– retain

– copy

Alloc is a static method from NSObject and it’s the closest thing to the keyword NEW in ActionScript. When you alloc with autorelease you write something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 
@synthesize totalLength = _totalLength, angle = _angle;
 
@synthesize points = _points;
 
-(id) init {
 
	self = [super init];
 
	if (self != nil) {
 
		_points = [[NSMutableArray array] retain];
 
		_totalLength = 0;
 
        _angle = 0;
 
	}
 
	return self;
 
}

If you use retain instead of autorelease you store a reference to that object in memory and therefore that object will remain in memory until you destroy that reference.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 
-(void) appendPoint:(CGPoint) point {
 
	[self insertPoint:point atIndex:[_points count] andSkipOrganize:NO];
 
}
 
-(void) insertPoint:(CGPoint)point atIndex:(NSInteger)index andSkipOrganize:(BOOL)skipOrganize {
 
    if ([_points count] == 0) {
 
        [_points addObject:[PathPoint withPoint:point]];
 
    } else {
 
        [_points insertObject:[PathPoint withPoint:point] atIndex:index];
 
    }
 
	;
 
    //NSLog(@"Length: %i", [_points count]);
 
	if (!skipOrganize) {
 
		[self organize];
 
	}
 
}
 
-(void) insertMultiplePoints:(NSMutableArray *) points atIndex:(NSInteger) index {
 
 
 
	NSInteger len = [points count];
 
	for (int i = 0; i < len; i++) {
 
		NSValue *value = [points objectAtIndex:i];
 
		CGPoint point = [value CGPointValue];
 
		[self insertPoint:point atIndex:(index + i) andSkipOrganize:YES];
 
	}
 
}

The important thing to understand is what creates a “retain effect.” Calling retain does it, calling copy does it. But also when you add an object to an array, or some kind of collection or container object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
 
-(void) organize {
 
	_totalLength = 0;
 
	NSInteger last = [_points count] - 1;
 
	if (last == -1) {
 
		_first = nil;
 
	} else if (last == 0) {
 
		_first = [_points objectAtIndex:0];
 
		_first.progress = _first.xChange = _first.yChange = _first.length = 0;
 
		return;
 
	}
 
	PathPoint *pp;
 
	for (int i = 0; i <= last; i++) {
 
		if ([_points objectAtIndex:i] != NULL) {
 
			pp = [_points objectAtIndex:i];
 
			pp.x = pp.point.x;
 
			pp.y = pp.point.y;
 
			if (i == last) {
 
				pp.length = 0;
 
				pp.next = NULL;
 
			} else {
 
				pp.next = [_points objectAtIndex: (i+1)];
 
				pp.xChange = pp.next.x - pp.x;
 
				pp.yChange = pp.next.y - pp.y;
 
				pp.length = sqrt(pow(pp.xChange, 2) + pow(pp.yChange, 2));
 
				_totalLength += pp.length;
 
			}
 
		}
 
	}
 
	_first = pp = [_points objectAtIndex:0];
 
	float curTotal = 0;
 
 
 
	while (pp != nil) {
 
		pp.progress = curTotal / _totalLength;
 
		curTotal += pp.length;
 
		pp = pp.next;
 
	}
 
 
 
	[self updateAngles];
 
 
 
}
 
-(void) updateAngles {
 
	PathPoint *pp = _first;
 
	while (pp) {
 
		pp.angle = -1 * atan2(pp.yChange, pp.xChange) * RAD2DEG;
 
        pp = pp.next;
 
 
 
	}
 
}

Objects are only deleted or freed from memory if their reference or retain count equals 0. If the retain count is not equal to 0, but the code has no longer any reference to the memory address (no pointers) you have what is called a Mempory Leak. And these are bad!

One very useful method to help you keep track of things is the retainCount method, another method from NSObject, that returns the number of references stored for an object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 
-(NSValue*) getPointAtProgress:(float) progress {
 
	progress = fabs(progress);
 
	if (progress > 1) progress = 1;
 
	PathPoint *pp = _first;
 
 
 
	if (pp.next == nil) return nil;
 
	if (!pp.next.progress) return nil;
 
	while (pp.next != nil && pp.next.progress < progress) { 
 
		pp = pp.next;
 
	}
 
	if (pp != nil) {
 
		_angle = pp.angle;
 
		float pathProg = ( progress - pp.progress) / (pp.length / _totalLength);
 
        return [NSValue valueWithCGPoint:CGPointMake(pp.x + pathProg * pp.xChange, pp.y + pathProg * pp.yChange)];
 
	}
 
	return nil;
 
 
 
}

So the rule is: don’t create more references (pointers) than you need, and remember to clear them out. I will point out all the different strategies I’ve used in this simple application as they occur.

The important thing here, as far as properties go, is that when you use the retain or copy keyword in a property declaration such as:

1
2
 
[NSValue valueWithCGPoint:CGPointMake(pp.x + pathProg * pp.xChange, pp.y + pathProg * pp.yChange)];

You are telling the compiler to create a SETTER that will store a reference to that data, because you want it to remain in memory until you get rid of it.

I didn’t need to use retain for “next” in the actual code because this variable should only point to an already retained object, the next PathPoint object in the path. You must pay attention to your code and focus, so that you don’t end up creating multiple retains for things you don’t need and can’t release later. So be clever! Trace out the value for retainCount in every variable you are not sure of and if something is not right just go figure out where the extra retain is.

PathPoint.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
 
-(void) renderObject:(CCSprite *)object at:(float) progress {
 
	if (progress > 1) {
 
		progress -= floor(progress);
 
	} else if (progress < 0) {
 
		progress -= floor(progress) - 1;
 
	}
 
	if (_first == nil) {
 
		return;
 
	}
 
	PathPoint *pp = _first;
 
	while (pp.next != nil && pp.next.progress < progress) {
 
		pp = pp.next;
 
	}
 
	if (pp != nil) {
 
		_angle = pp.angle;
 
 
 
		float pathProg =  (progress - pp.progress) / (pp.length / _totalLength);
 
 
 
        object.position = CGPointMake(pp.x + pathProg * pp.xChange, pp.y + pathProg * pp.yChange);		
 
	}
 
 
 
}
 
-(void) setPoints:(NSMutableArray *) points {
 
	[_points removeAllObjects];
 
	[self insertMultiplePoints:points atIndex:0];
 
}
 
-(void) dealloc {
 
 
 
	[_points release];
 
	[super dealloc];
 
}

In the implementation of the class, notice that I don’t have to import the things already imported in the interface file, I just need to import the interface.

Then I synthesize the properties, in essence this creates getters and setters for each property. So now I can use dot notation to refer to these variables.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
 
package  {
 
 
 
	import flash.geom.Matrix;
 
	import flash.geom.Point;
 
	public class LinePath2D {		
 
		private static const _RAD2DEG:Number = 180 / Math.PI;
 
		private static const _DEG2RAD:Number = Math.PI / 180;
 
		public var angle:Number;
 
		private var _first:PathPoint;
 
		private var _points:Array;
 
		private var _totalLength:Number;
 
 
 
		public function LinePath2D() {
 
			super();
 
			_points = [];
 
			_totalLength = 0;
 
		}
 
 
 
		public function appendPoint(point:Point):void {
 
			_insertPoint(point, _points.length, false);
 
		}
 
 
 
		private function _insertPoint(point:Point, index:uint, skipOrganize:Boolean):void {
 
			_points.splice(index, 0, new PathPoint(point));
 
			if (!skipOrganize) {
 
				_organize();
 
			}
 
		}
 
 
 
		public function insertMultiplePoints(points:Array, index:uint=0):void {
 
			var l:int = points.length;
 
			for (var i:int = 0; i < l; i++) {
 
				_insertPoint(points[i], index + i, true);
 
			}
 
		}
 
 
 
		private function _organize():void {
 
			_totalLength = 0;
 
			var last:int = _points.length - 1;
 
			if (last == -1) {
 
				_first = null;
 
			} else if (last == 0) {
 
				_first = _points[0];
 
				_first.progress = _first.xChange = _first.yChange = _first.length = 0;
 
				return;
 
			}
 
			var pp:PathPoint;
 
			for (var i:int = 0; i <= last; i++) { 
 
				if (_points[i] != null) {
 
					pp = _points[i];
 
					pp.x = pp.point.x;
 
					pp.y = pp.point.y;
 
					if (i == last) {
 
						pp.length = 0;
 
						pp.next = null;
 
					} else {
 
						pp.next = _points[i + 1];
 
						pp.xChange = pp.next.x - pp.x;
 
						pp.yChange = pp.next.y - pp.y;
 
						pp.length = Math.sqrt(pp.xChange * pp.xChange + pp.yChange * pp.yChange);
 
						_totalLength += pp.length;
 
					}
 
				}
 
			}
 
			_first = pp = _points[0];
 
			var curTotal:Number = 0;
 
			while (pp) {
 
				pp.progress = curTotal / _totalLength;
 
				curTotal += pp.length;
 
				pp = pp.next;
 
			}
 
			_updateAngles();
 
		}
 
 
 
		private function _updateAngles():void {
 
			var pp:PathPoint = _first;
 
			while (pp) {
 
				pp.angle = Math.atan2(pp.yChange, pp.xChange ) * _RAD2DEG;
 
				pp = pp.next;
 
			}
 
 
 
		}
 
 
 
		public function getPointAtProgress (progress:Number):Point {
 
			progress = Math.abs(progress);
 
 
 
			var point:Point = new Point();
 
			if (progress > 1) progress = 1;
 
			var pp:PathPoint = _first;
 
			if (!pp.next) return null;
 
			if (!pp.next.progress) return null;
 
 
 
			while (pp.next != null && pp.next.progress < progress) {
 
				pp = pp.next;
 
			}
 
 
 
			if (pp != null) {
 
				angle = pp.angle;
 
				var pathProg:Number = (progress - pp.progress) / (pp.length / _totalLength);
 
				point.x = pp.x + pathProg * pp.xChange;
 
				point.y = pp.y + pathProg * pp.yChange;
 
 
 
				return point;
 
 
 
			}
 
			return null;
 
		}
 
 
 
		public function renderObjectAt(target:Object, progress:Number, autoRotate:Boolean=false, rotationOffset:Number=0):void {
 
			if (progress > 1) {
 
				progress -= int(progress);
 
			} else if (progress < 0) {
 
				progress -= int(progress) - 1;
 
			}
 
			if (_first == null) {
 
				return;
 
			}
 
 
 
			var pp:PathPoint = _first;
 
			while (pp.next != null && pp.next.progress < progress) {
 
				pp = pp.next;
 
			}
 
 
 
			if (pp != null) {
 
				angle = pp.angle;
 
				var pathProg:Number = (progress - pp.progress) / (pp.length / _totalLength);
 
				var px:Number = pp.x + pathProg * pp.xChange;
 
				var py:Number = pp.y + pathProg * pp.yChange;
 
 
 
				target.x = px ;
 
				target.y = py ;
 
 
 
			}
 
 
 
		}
 
 
 
		public function get totalLength():Number {
 
			return _totalLength;
 
		}
 
 
 
		public function get points():Array {
 
			return _points;
 
		}
 
		public function set points(value:Array):void {
 
			_points = [];
 
			insertMultiplePoints(value, 0);
 
		}
 
 
 
 
 
	}
 
}

Then comes the method implementations. You don’t have to include every method in your interface file, but it is good practice and it gets rid of annoying warnings in XCode (IMPORTANT: if you later extend this class, then only methods in the interface will be inherited!). And of course you should not add methods from your super class in the interface file but simply override them in the implementation file.

The plus sign next to the return type denotes a static method or class method. The minus sign denotes an instance method.

I chose to create the initialization process for this class like I did here in order to show you as much as I could on that subject, you wouldn’t actually need the static method “withPoint.”

I will explain the methods in a bit.

The Brackets Thing

Brackets mean: this is Objective-C. The stuff outside the brackets is probably C.

You can have methods written in C. In fact we will use a bunch of them. Whenever you use parentheses to pass arguments in your method, this method is written in C (or C++).

So these are probably C:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
 
#import <Foundation/Foundation.h>
 
@class CCSprite;
 
@class PathPoint;
 
@interface LinePath2D : NSObject {
 
 
 
	float _angle;
 
	NSMutableArray *_points;
 
	float _totalLength;
 
	PathPoint *_first;
 
}
 
@property float angle;
 
@property float totalLength;
 
@property (nonatomic, assign) NSMutableArray *points;
 
-(void) appendPoint:(CGPoint) point;
 
-(void) insertPoint:(CGPoint) point atIndex:(NSInteger)index andSkipOrganize:(BOOL)skipOrganize;
 
-(void) insertMultiplePoints:(NSMutableArray *) points atIndex:(NSInteger) index;
 
-(void) organize;
 
-(void) updateAngles;
 
-(NSValue *) getPointAtProgress:(float) progress;
 
-(void) renderObject:(CCSprite *) object at:(float) progress;
 
@end

These are Objective-C:

1
2
3
4
5
6
7
8
 
-(void) insertMultiplePoints:(NSMutableArray *) points atIndex:(NSInteger) index;
 
-(NSValue *) getPointAtProgress:(float) progress;
 
//In castings too
 
NSString *valueFrom = (NSString *) myObjectX;

Here are some ActionScript methods rewritten in the Objective-C syntax:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
 
#import "LinePath2D.h"
 
#import "PathPoint.h"
 
#import "cocos2d.h"
 
#define RAD2DEG  180/M_PI;
 
@implementation LinePath2D
 
@synthesize totalLength = _totalLength, angle = _angle;
 
@synthesize points = _points;
 
-(id) init {
 
	self = [super init];
 
	if (self != nil) {
 
		_points = [[NSMutableArray array] retain];
 
		_totalLength = 0;
 
        _angle = 0;
 
	}
 
	return self;
 
}
 
-(void) appendPoint:(CGPoint) point {
 
	[self insertPoint:point atIndex:[_points count] andSkipOrganize:NO];
 
}
 
-(void) insertPoint:(CGPoint)point atIndex:(NSInteger)index andSkipOrganize:(BOOL)skipOrganize {
 
    if ([_points count] == 0) {
 
        [_points addObject:[PathPoint withPoint:point]];
 
    } else {
 
        [_points insertObject:[PathPoint withPoint:point] atIndex:index];
 
    }
 
	;
 
    //NSLog(@"Length: %i", [_points count]);
 
	if (!skipOrganize) {
 
		[self organize];
 
	}
 
}
 
-(void) insertMultiplePoints:(NSMutableArray *) points atIndex:(NSInteger) index {
 
 
 
	NSInteger len = [points count];
 
	for (int i = 0; i < len; i++) {
 
		NSValue *value = [points objectAtIndex:i];
 
		CGPoint point = [value CGPointValue];
 
		[self insertPoint:point atIndex:(index + i) andSkipOrganize:YES];
 
	}
 
}
 
-(void) organize {
 
	_totalLength = 0;
 
	NSInteger last = [_points count] - 1;
 
	if (last == -1) {
 
		_first = nil;
 
	} else if (last == 0) {
 
		_first = [_points objectAtIndex:0];
 
		_first.progress = _first.xChange = _first.yChange = _first.length = 0;
 
		return;
 
	}
 
	PathPoint *pp;
 
	for (int i = 0; i <= last; i++) {
 
		if ([_points objectAtIndex:i] != NULL) {
 
			pp = [_points objectAtIndex:i];
 
			pp.x = pp.point.x;
 
			pp.y = pp.point.y;
 
			if (i == last) {
 
				pp.length = 0;
 
				pp.next = NULL;
 
			} else {
 
				pp.next = [_points objectAtIndex: (i+1)];
 
				pp.xChange = pp.next.x - pp.x;
 
				pp.yChange = pp.next.y - pp.y;
 
				pp.length = sqrt(pow(pp.xChange, 2) + pow(pp.yChange, 2));
 
				_totalLength += pp.length;
 
			}
 
		}
 
	}
 
	_first = pp = [_points objectAtIndex:0];
 
	float curTotal = 0;
 
 
 
	while (pp != nil) {
 
		pp.progress = curTotal / _totalLength;
 
		curTotal += pp.length;
 
		pp = pp.next;
 
	}
 
 
 
	[self updateAngles];
 
 
 
}
 
-(void) updateAngles {
 
	PathPoint *pp = _first;
 
	while (pp) {
 
		pp.angle = -1 * atan2(pp.yChange, pp.xChange) * RAD2DEG;
 
        pp = pp.next;
 
 
 
	}
 
}
 
-(NSValue*) getPointAtProgress:(float) progress {
 
	progress = fabs(progress);
 
	if (progress > 1) progress = 1;
 
	PathPoint *pp = _first;
 
 
 
	if (pp.next == nil) return nil;
 
	if (!pp.next.progress) return nil;
 
	while (pp.next != nil && pp.next.progress < progress) { 
 
		pp = pp.next;
 
	}
 
	if (pp != nil) {
 
		_angle = pp.angle;
 
		float pathProg = ( progress - pp.progress) / (pp.length / _totalLength);
 
        return [NSValue valueWithCGPoint:CGPointMake(pp.x + pathProg * pp.xChange, pp.y + pathProg * pp.yChange)];
 
	}
 
	return nil;
 
 
 
}
 
-(void) renderObject:(CCSprite *)object at:(float) progress {
 
	if (progress > 1) {
 
		progress -= floor(progress);
 
	} else if (progress < 0) {
 
		progress -= floor(progress) - 1;
 
	}
 
	if (_first == nil) {
 
		return;
 
	}
 
	PathPoint *pp = _first;
 
	while (pp.next != nil && pp.next.progress < progress) {
 
		pp = pp.next;
 
	}
 
	if (pp != nil) {
 
		_angle = pp.angle;
 
 
 
		float pathProg =  (progress - pp.progress) / (pp.length / _totalLength);
 
 
 
        object.position = CGPointMake(pp.x + pathProg * pp.xChange, pp.y + pathProg * pp.yChange);		
 
	}
 
 
 
}
 
-(void) setPoints:(NSMutableArray *) points {
 
	[_points removeAllObjects];
 
	[self insertMultiplePoints:points atIndex:0];
 
}
 
-(void) dealloc {
 
 
 
	[_points release];
 
 
 
	[super dealloc];
 
}
 
@end

I added the last one on purpose. A method name and its arguments form something called a SELECTOR. In Objective-C a method’s argument can have an optional property name, as well as the name used within the method like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 
@synthesize totalLength = _totalLength, angle = _angle;
 
@synthesize points = _points;
 
-(id) init {
 
	self = [super init];
 
	if (self != nil) {
 
		_points = [[NSMutableArray array] retain];
 
		_totalLength = 0;
 
        _angle = 0;
 
	}
 
	return self;
 
}

Yes, it creates huge method names but they are easier to read. Mind you, this could have been written as:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 
-(void) appendPoint:(CGPoint) point {
 
	[self insertPoint:point atIndex:[_points count] andSkipOrganize:NO];
 
}
 
-(void) insertPoint:(CGPoint)point atIndex:(NSInteger)index andSkipOrganize:(BOOL)skipOrganize {
 
    if ([_points count] == 0) {
 
        [_points addObject:[PathPoint withPoint:point]];
 
    } else {
 
        [_points insertObject:[PathPoint withPoint:point] atIndex:index];
 
    }
 
	;
 
    //NSLog(@"Length: %i", [_points count]);
 
	if (!skipOrganize) {
 
		[self organize];
 
	}
 
}
 
-(void) insertMultiplePoints:(NSMutableArray *) points atIndex:(NSInteger) index {
 
 
 
	NSInteger len = [points count];
 
	for (int i = 0; i < len; i++) {
 
		NSValue *value = [points objectAtIndex:i];
 
		CGPoint point = [value CGPointValue];
 
		[self insertPoint:point atIndex:(index + i) andSkipOrganize:YES];
 
	}
 
}

But the longest form has all the elements recommended by Apple: The use of the optional property aliases (withHeight, atX, atY, withColor) and with the name of the first property attached to the name of the method (createBoxWithWidth). And therefore, the actual name of this method in Objective-C is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
 
-(void) organize {
 
	_totalLength = 0;
 
	NSInteger last = [_points count] - 1;
 
	if (last == -1) {
 
		_first = nil;
 
	} else if (last == 0) {
 
		_first = [_points objectAtIndex:0];
 
		_first.progress = _first.xChange = _first.yChange = _first.length = 0;
 
		return;
 
	}
 
	PathPoint *pp;
 
	for (int i = 0; i <= last; i++) {
 
		if ([_points objectAtIndex:i] != NULL) {
 
			pp = [_points objectAtIndex:i];
 
			pp.x = pp.point.x;
 
			pp.y = pp.point.y;
 
			if (i == last) {
 
				pp.length = 0;
 
				pp.next = NULL;
 
			} else {
 
				pp.next = [_points objectAtIndex: (i+1)];
 
				pp.xChange = pp.next.x - pp.x;
 
				pp.yChange = pp.next.y - pp.y;
 
				pp.length = sqrt(pow(pp.xChange, 2) + pow(pp.yChange, 2));
 
				_totalLength += pp.length;
 
			}
 
		}
 
	}
 
	_first = pp = [_points objectAtIndex:0];
 
	float curTotal = 0;
 
 
 
	while (pp != nil) {
 
		pp.progress = curTotal / _totalLength;
 
		curTotal += pp.length;
 
		pp = pp.next;
 
	}
 
 
 
	[self updateAngles];
 
 
 
}
 
-(void) updateAngles {
 
	PathPoint *pp = _first;
 
	while (pp) {
 
		pp.angle = -1 * atan2(pp.yChange, pp.xChange) * RAD2DEG;
 
        pp = pp.next;
 
 
 
	}
 
}

This is said to be the SELECTOR of the method.

One weird thing is when you have more than one selector in a method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 
-(NSValue*) getPointAtProgress:(float) progress {
 
	progress = fabs(progress);
 
	if (progress > 1) progress = 1;
 
	PathPoint *pp = _first;
 
 
 
	if (pp.next == nil) return nil;
 
	if (!pp.next.progress) return nil;
 
	while (pp.next != nil && pp.next.progress < progress) { 
 
		pp = pp.next;
 
	}
 
	if (pp != nil) {
 
		_angle = pp.angle;
 
		float pathProg = ( progress - pp.progress) / (pp.length / _totalLength);
 
        return [NSValue valueWithCGPoint:CGPointMake(pp.x + pathProg * pp.xChange, pp.y + pathProg * pp.yChange)];
 
	}
 
	return nil;
 
 
 
}

The Init and the ID thingies

The initialization process has one very important function: it returns a working instance of the class. The second function is optional, and that is to set default values for your properties.

The Objective-C documentation from Apple recommends that if you have more than one way to instantiate a class, you should always select one init method to be your main initialization method. All other methods for initialization should cascade eventually back to that main method. Confusing? Here’s what I mean:

You have a class called Lemonade. And this class has two different initialization methods. You can:

1
2
 
[NSValue valueWithCGPoint:CGPointMake(pp.x + pathProg * pp.xChange, pp.y + pathProg * pp.yChange)];

The main method, I decided as the developer, will be the initWithSlicedLemon. This is the method that actually creates the instance. The other will slice the lemon object first and then pass it to initWithSlicedLemon.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
 
-(void) renderObject:(CCSprite *)object at:(float) progress {
 
	if (progress > 1) {
 
		progress -= floor(progress);
 
	} else if (progress < 0) {
 
		progress -= floor(progress) - 1;
 
	}
 
	if (_first == nil) {
 
		return;
 
	}
 
	PathPoint *pp = _first;
 
	while (pp.next != nil && pp.next.progress < progress) {
 
		pp = pp.next;
 
	}
 
	if (pp != nil) {
 
		_angle = pp.angle;
 
 
 
		float pathProg =  (progress - pp.progress) / (pp.length / _totalLength);
 
 
 
        object.position = CGPointMake(pp.x + pathProg * pp.xChange, pp.y + pathProg * pp.yChange);		
 
	}
 
 
 
}
 
-(void) setPoints:(NSMutableArray *) points {
 
	[_points removeAllObjects];
 
	[self insertMultiplePoints:points atIndex:0];
 
}
 
-(void) dealloc {
 
 
 
	[_points release];
 
	[super dealloc];
 
}

The id type is the equivalent of an * type in ActionScript. Init methods should aways return id.

The main init method, the one called last in the long chain of init methods you may create, should call its superclass init method.

But most of the time you will have one init method and this will override the superclass init and you will end up with something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 
-(id) init {
 
	self  = [super init];
 
	if (self != nil) {
 
		//init default values
 
		myProperty1 = "The Fonz";
 
		myProperty2 = "heeeeeeyyyy!";
 
	}
 
	return self;
 
}

Sometimes one of these init methods will be a static method, called Class methods in Objective-C. There are some advantages to this depending on what kind of checks you want to perform in the initialization process. Here’s why.

1
2
3
4
 
//when I create an instance with an instance init method I must allocate it first
 
Lemonade *juice = [[[Lemonade alloc] initWithSlicedLemon:myLemons] retain];

But if the initialization doesn’t work for some reason, say I send apples instead of lemons, then the object must be released. Notice that memory is allocated first and then the object is initialized, but if init fails, the memory may take a while to be freed again. But with a static method you can perform checks before allocating:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 
//the plus sign means this is static
 
+(id) withLemon:(Lemon) lemon {
 
	//run checks before allocating
 
	if (lemon is in fact a Lemon) {
 
		//call the instance init now
 
		return [[Lemonade alloc] initWithLemon:Lemon];
 
	}
 
}

In PathPoint I used a static method just to show off.

But here is one very important thing!!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 
//this is a static method. SELF here means the CLASS!
 
+(id) withPoint:(CGPoint) initPoint {
 
	return [[[self alloc] initWithPoint:initPoint] autorelease];
 
}
 
//this is an instance method. SELF here means the Instance!
 
-(id) initWithPoint:(CGPoint) initPoint {
 
	self = [super init];
 
	...
 
}

The comments explain what is important to remember. The Objective-C documentation recommends the use of SELF in the static method like I used it here. The reason is the inheritance chain, but not actually important for this tutorial, just remember that using self alloc there is the right way to go. And also remember that self means the Class in that method.

Just like any other static method, you cannot access any instance property from within the static method unless you create and allocate a temporary instance of the class to grab the values from. Also very useful sometimes.

Notice too that you can return an object set for autorelease. This is pretty cool and solves one big puzzle beginners in Objective-C have. And that is how to return an object without retaining it first.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 
//the puzzle!
 
-(SomeObject) getMeThatObject {
 
	SomeObject *myObject = [[[SomeObject alloc] init] autorelease];
 
	return myObject;
 
	//this may not work the way you want it to
 
}
 
-(SomeObject) getMeThatObject {
 
	SomeObject *myObject = [[[SomeObject alloc] init] retain];
 
	return myObject;
 
	[myObject release];
 
	//this of course will not work at all
 
}
 
//But this!
 
-(SomeObject) getMeThatObject {
 
	SomeObject *myObject = [[SomeObject alloc] init];
 
	return [myObject autorelease];
 
}
 
//then when function is called you retain or copy
 
SomeObject *myNewObject = [[self getMeThatObject] retain];

But of course anything to do with RETAIN or AUTORELEASE will depend on what you are doing and trying to accomplish. So don’t go thinking what I showed here is the absolute fool-proof way of initializing objects. I took some risks with autorelease to be honest, but only because I knew what would retain what later.

The PathPoint gets added to an Array right after it is instantiated so I don’t want to keep anything in its allocation process. The one retain count I will get will come from the array and that’s it. That’s all I want. So I autorelease the one I used in the instantiation. (Again I could not use release there because of the return.)

Lastly the dealloc…

The Dealloc thing

This is inherited from NSObject and you use it to release retained memory. Think of it as the REMOVED_FROM_STAGE event. And you must always call dealloc in the super class.

NEXT: The LinePath2D Class