Sie sind auf Seite 1von 207

These are confidential sessionsplease refrain from streaming, blogging, or taking pictures

Your second-most important goal as a developer


Session 112
Writing Easy-To-Change Code
Ken Kocienda
Principal Engineer, iOS Apps and Frameworks
1
This is a talk about writing code
2
Easy to read
learn
understand
maintain
change
3
Easy to change software
4
Your second-most important goal
5
What is your most important goal?
6
Ship products!
7
This is how we think at Apple
8
Over 30 iOS releases since 2007
9
Releases are complicated
Legal
Marketing
Bug fixes
New app features
Improving existing features
Changing priorities
Competition
Tight schedules
Too few people
Testing
New hardware
New OS features
Work with other companies
Too many people
App Store submission
10
Help you make change easier
11
You always change your software
12
What Kinds of Change?
Bug fixes
Adding new features
Enhancing existing features
Changing code someone else wrote
Changing code you wrote six months ago
13
General conventions
14
Mac and iOS conventions
15
Things to think about
Topics
16
Style
Stories
Laziness
Hygiene
Notifications
Topics
Optimization
Dependencies
Mixing
Expectations
Wrap up
17
More than skin deep
Style
18
Coding conventions
19
Coding Conventions
Brace style for if-else
Parenthesis Style
Leading underscores
Code indenting
CapitalizationStyle (i.e. capitalization_style)
20
Local consistency is important
21
The beginnings of style
22
Style goes deeper
23
People think that I can teach
them style. What stuff it all is! Have
something to say, and say it as
clearly as you can. That is the only
secret of style.
Matthew Arnold
24
Clarity
25
Clear writing is easier to understand
26
Clear code is easier to change
27
Elements of a clear coding style?
28
!
!
Good names
Common idioms
29
!
!
Good names
Common idioms
30
Good Names Are Descriptive
NSString *searchString = [self _searchString];
BOOL searchStringIsNotEmpty = [searchString length] != 0;
if (searchStringIsNotEmpty) {
[self _findBanner]->findString(searchString,
shouldBeep ? BeepOnFailure : DoNotBeepOnFailure);
}
31
Good Names Are Descriptive
NSString *searchString = [self _searchString];
BOOL searchStringIsNotEmpty = [searchString length] != 0;
if (searchStringIsNotEmpty) {
[self _findBanner]->findString(searchString,
shouldBeep ? YES : NO);
}
32
Descriptive Names
You can go overboard
@interface YesYouCanMakeNamesForClassesWhichAreTooLong
{
id _aReallyVerboseNameJustToBePerfectlyClear;
}
@end
33
Bad Names? Boolean Arguments
Hard to know what they mean
[magnifier stopMagnifying:NO];
34
Bad Names? Boolean Arguments
Hard to know what they mean
- (void)stopMagnifying:(BOOL)animated;
35
Bad Names? Boolean Arguments
Hard to know what they mean
- (void)stopMagnifyingAnimated:(BOOL)animated;
36
Good names are descriptive
37
!
!
Good names
Common idioms
38
!
!
Good names
Common idioms
39
Workhorse Lines of Code
Hard to know what they mean
[_rightView setAlpha:![[_temporary text] length] ? 1.0 : 0.0];
40
Count square brackets?
41
Workhorse Lines of Code
Hard to know what they mean
[_rightView setAlpha:![[_temporary text] length] ? 1.0 : 0.0];
42
Rewrite Workhorse Lines of Code
Be clear!
BOOL textIsEmpty = [_temporary.text length] == 0;
float alpha = textIsEmpty ? 1.0 : 0.0;
[_rightView setAlpha:alpha];
43
Read and understand quickly
44
Design patterns
45
Design Patterns
Very common patterns
Singleton
Observer
Prototype
Chain of responsibility
Command
46
Design Patterns
Patterns used in Apple frameworks
MVC
Target-action
Delegation
Autorelease
View controller
47
Idioms communicate at a high level
48
Shared vocabulary
49
!
!
Good names
Common idioms
50
More than skin deep
Style
51
Now I understand
Stories
52
Bug. Why?
53
Did not anticipate
Did not understand
54
Debug
55
Everyone knows that debugging is
twice as hard as writing a program in
the first place. So if you are as clever
as you can be when you write it, how
will you ever debug it?
Brian Kernighan
56
Step 1: Debugger
57
What are you really looking for?
58
Step 1: Debugger
T
h
i
n
k
59
How could this bug happen?
60
The most effective debugging tool is
still careful thought, coupled with
judiciously placed print statements.
Brian Kernighan
61
Debugging is understanding
62
Debugging is not jiggling code
63
-performSelector:withObject:afterDelay:
Rarely right!
64
Why?
[self foo];
[self bar];
65
Each bug fix should tell a story
66
Investigate. Eureka!
Tell someone before you code the fix
67
Tell the story during code review
68
Write the story into your bug tracker
69
Anticipate more
Understand better
70
Now I understand
Stories
71
Wake me when it is over
Laziness
72
Lazy initialization
73
It is good
74
It is not magic
75
Singleton Objects
Lazy initialization is common
FooController *controller = [FooController sharedInstance];
76
Singleton Objects
Lazy initialization is common
@implementation FooController
+ (FooController *)sharedInstance
{
static dispatch_once_t once;
static FooController *instance;
dispatch_once(&once, ^{
instance = [[FooController alloc] init];
});
return instance;
}
@end
77
Singleton Objects
Lazy initialization is common
@implementation FooController (Continued)
- (id)init
{
...
BarController *barController = [BarController
sharedInstance];
...
}
@end
78
Singleton Objects
Lazy initialization is common
@implementation BarController
- (id)init
{
...
FooController *fooController = [FooController
sharedInstance];
...
}
@end
79
Init storm
80
Several problems
81
Long pause
82
Order of initialization
83
Singleton Objects
How many do you have?
@implementation FooController
+ (FooController *)sharedInstance
{
static dispatch_once_t once;
static FooController *instance;
dispatch_once(&once, ^{
instance = [[FooController alloc] init];
});
return instance;
}
@end
84
Singleton Objects
How many do you have?
@implementation FooController
+ (FooController *)sharedInstance
{
static FooController *instance;
if (!instance)
instance = [[FooController alloc] init];
return instance;
}
@end
85
Multiple instantiation of singleton
86
Mess
87
Think through lazy initialization
88
No silver bullets
89
Lightweight alloc at program start
Better singleton decomposition
90
Alternative accessor patterns
91
Alternative Accessor Patterns
Create or not?
@interface FooController
+ (FooController *)sharedInstance; // will create
+ (FooController *)activeInstance; // wont create
+ (FooController *)sharedInstance
createIfNeeded:(BOOL)createIfNeeded;
@end
92
Wake me when it is over
Laziness
93
You make the mess you clean it up!
Hygiene
94
Good hygiene takes effort
95
The best writing is rewriting.
E.B. White
96
Do not throw away code
97
Conflict?
98
Changes are part of a process
99
Your top priority should be to ship
100
Do not rewrite refactor
101
Refactoring
Keep functionality, but change form
102
What about cruft?
103
Cruft is not
code you do not understand
code you did not write
code you do not like
104
What is genuine cruft?
105
What Is Genuine Cruft?
Dead code
Comments which no longer apply
There is no number three
106
Use compiler for dead code checks
107
Delete or check old comments
108
Accumulated knowledge
109
Size of change is important
110
Small: clean up as you go
111
Medium: need coordination
112
Large: need real planning
113
Beware of regressions
114
Test!
115
You make the mess you clean it up!
Hygiene
116
Open the window and holler!
Notifications
117
goto
118
Notifications are a glorified goto
119
You do not even say where to go!
120
You can go to more than one place!
121
Frustrate code inspection
You can not see what code will run
122
Non-deterministic behavior
Callbacks are unordered
123
Notifications can complicate change
124
Not all bad
125
Notifications promote loose coupling
126
Model/View/Controller (MVC)
CoreData
127
will/did
128
You should know about endpoints
129
Think twice about other uses
130
Code can be too loosely coupled
131
Consider protocols or delegates
132
goto NextTopic;
133
The 3% solution
Optimization
134
We should forget about small
efficiencies, say about 97% of the time:
premature optimization is
the root of all evil.
Donald Knuth
97% of the time
135
97% of the time
100% - 97% = 3%
136
Which 3% to worry about?
137
Things Which Can Be Slow
Memory allocation
View creation
Drawing
Questionable algorithms
Questionable data structures
I/O
Blocking on information
Unnecessary work
New work you just added
138
Optimize when you have measured
139
Use Instruments
140
Optimize when you understand
141
Optimize code with clearest role
142
Optimize slowest and oldest 3%
143
Keep new code easiest to change
144
Trades are OK!
Never make the program slower
145
Change, test, measure, optimize
146
The 3% solution
Optimization
147
Dont call us well call you
Dependencies
148
Implications of change
149
Limit collateral damage
150
!
!
Inheritance trees
Call graphs
151
!
!
Inheritance trees
Call graphs
152
Shallow is better
153
Avoid layers of overridden methods
154
Use delegation
155
Delegation
Customize by calling another object
Keeps conceptual overhead small
Vary customization at runtime as needed
156
!
!
Inheritance trees
Call graphs
157
Smaller is better
158
Limit includes
159
You get faster compile times
160
Strive for unidirectional calling
161
Bidirectional Call Graph
We are all friends here
Foo
@interface {
Bar *_b;
}
- (void)foo;
@end
@implementation
[_b bar];
@end
Bar
@interface {
Foo *_f;
}
- (void)bar;
@end
@implementation
[_f foo];
@end
162
Bidirectional Call Graph
We are all friends here
Foo
@interface {
Bar *_b;
}
- (void)foo:(id)arg;
@end
@implementation
[_b bar];
@end
Bar
@interface {
Foo *_f;
}
- (void)bar;
@end
@implementation
[_f foo];
@end
163
Bidirectional Call Graph
We are all friends here
Foo
@interface {
Bar *_b;
}
- (void)foo:(id)arg;
@end
@implementation
[_b bar];
@end
Bar
@interface {
Foo *_f;
}
- (void)bar;
@end
@implementation
[_f foo:arg];
@end
164
Unidirectional Call Graph
Rethink relationship
Master
@interface {
Slave *_s;
}
- (void)changed;
@end
@implementation
[_s update:arg];
@end
Slave
@interface {
}
- (void)update:(id)arg;
@end
@implementation
@end
165
Unidirectional Call Graph
Rethink relationship
Master
@interface {
Slave *_s;
}
- (void)dataReceived;
- (void)processData;
@end
@implementation
[_s update:arg];
@end
Slave
@interface {
}
- (void)update:(id)arg;
@end
@implementation
@end
166
!
!
Inheritance trees
Call graphs
167
Dont call us well call you
Dependencies
168
Purity of essence (OPE)
Mixing
169
Model/View/Controller (MVC)
170
Do not mix model and view changes
171
Do not mix different things
172
Computation and I/O
173
Algorithms and data sources
174
UI and a specific screen resolution
175
UX and an interface paradigm
176
Conflicted About Animation Arguments
Is this mixing too much?
- (void)setEditing:(BOOL)editing animated:(BOOL)animated;
177
Hard-code animations?
178
Multitasking gestures
179
We change how the system works
180
App launch without animating hard
181
Do not mix different things
182
Purity of essence (OPE)
Mixing
183
How do I work this thing?
Expectations
184
Bugs are often disappointments
185
I expected A, you did B
186
Be conservative in what you send;
be liberal in what you accept.
Jon Postel
187
Hard to use wrong
188
Method arguments
Assertions and early returns
189
Assertions
This will never work
// UIActionSheet.m
- (void)showInView:(UIView *)view
{
NSParameterAssert(view != nil);

...
}
190
Early Returns
The method will not run right now
- (void)beginWork
{
if (AlreadyBusy())
return;
...
}
191
What about ivars?
192
Global variables are bad, right?
193
Scope is too broad
194
ivar scope also can be too broad
195
Rules of Thumb for ivars
As few as possible
Simple life-cycles
Avoid tight relationships
Avoid letting non-setter methods change ivars
196
Hard to manage ivar state?
197
Use a state machine
198
UIGestureRecognizers
199
Multitasking gestures
200
State Machines
How do they help?
States help to think things through
States help to limit possibilities
States help to make assertions
Need to add a feature?
!
Add a state
!
Handle the transitions
201
Hard to use wrong
202
How do I work this thing?
Expectations
203
Ten things to think about
Wrap Up
204
Easy To Change Code
Ten things to think about
1. Write clear code
2. Bug fixes should tell a story
3. Keep control of lazy initialization
4. Refactor instead of rewriting
5. Use notifications for the right things
205
Easy To Change Code, (Cont.)
Ten things to think about
6. Keep new code easy to change
7. Optimize slowest and oldest code
8. Limit dependencies
9. Do not mix different things
10. Make code that is hard to use wrong
206
207

Das könnte Ihnen auch gefallen