forked from mudphone/ObjcPlayground
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathREADME
499 lines (406 loc) · 19.1 KB
/
README
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
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
Dynamic Languages - They're not just for Rubyists (and Pythonistas)
ABSTRACT
Objective-C is a popular language as it is a building block of all iOS applications. It also has powerful dynamic features, while running on C. This talk will introduce you to the basics of Objective-C, with an eye on how it is similar to other dynamic languages, such as Ruby.
WHY?
http://www.tiobe.com/index.php/paperinfo/tpci/Objective-C.html
Credit for the outline of this talk goes to:
- iOS 5 Programming Pushing the Limits, by Rob Napier, Mugunth Kumar
- Show a picture and link to book: http://iosptl.com/
Assumptions:
- Not covering Core Foundation
-
What We'll Cover:
- The Basics
- Objective-C w/ source code
- Mention of ARC
- objects and classes
- An intro to C Structs, and how Objective-C uses them
- instances, classes, superclasses, & meta-classes
- Categories, this is how they work.
- Messaging
- Basics: methods, messages, selectors, message sending
- Sending Messages
- What is a method?
- Disecting objc_msgSend()
- Dynamic usage
- @dynamic, dynamic loading, fast forwarding, normal forwarding, forwarding failure
- Core Data, relies on this
- A deep(er) dive into objc_msgSend
- Sidenote: The Objective-C Runtime
- What is it?
- How you interact with it
- Message forwarding examples
- Swizzling
- Method swizzling: swapping method implementations at runtime
- ISA swizzling : changing classes at runtime
- This is how KVO works
- That's cool, but what do I do now?
Basics / Intro:
- Primer? http://developer.apple.com/library/ios/#referencelibrary/GettingStarted/Learning_Objective-C_A_Primer/_index.html
- Like C++, no multiple inheritance, no operator overloading
- A Quick Intro to C Structs
- http://heather.cs.ucdavis.edu/~matloff/UnixAndC/CLanguage/PointersI.html
The Objective-C Object:
- All Objective-C objects are C structs
typedef struct objc_object {
Class isa;
} *id;
- ISA pointer (defines the object's class)
- Root class' ivars
- penultimate superclass' ivars
- ...
- superclass' ivars
- Class' ivars
- And the Class:
- The old way:
typedef struct objc_class *Class;
struct objc_class {
Class isa;
...
Classes and Metaclasses
- A class is like an object, you can pass it messages
- [MyClass alloc]
- Class methods are stored in the metaclass, which is where the Class isa pointer goes
- diagram: http://www.sealiesoftware.com/blog/archive/2009/04/14/objc_explain_Classes_and_metaclasses.html
- Class is instance of metaclass
- metaclass describes methods of class, just as class describes methods of instance
- Meta-classes are instances of the root class' metaclass
- which is also an instance of the root class' metaclass (ending in a cycle)
- Meta-classes superclasses are the meta-classes of their corresponding class' superclass
- but, the root meta-class' superclass is the root class
- so, class objects respond to the root class' instance methods
- Meta-classes are hidden from you ([NSObject class] #=> [NSObject self]), and are rarely accessed
- The new way (OS X 64 & iOS)
http://cocoawithlove.com/2010/01/getting-subclasses-of-objective-c-class.html
From objc-runtime-new.h
typedef struct class_ro_t {
...
const char * name;
const method_list_t * baseMethods;
const protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
...
const property_list_t *baseProperties;
} class_ro_t;
typedef struct class_rw_t {
...
const class_ro_t *ro;
method_list_t **methods;
struct chained_property_list *properties;
const protocol_list_t ** protocols;
struct class_t *firstSubclass;
struct class_t *nextSiblingClass;
} class_rw_t;
typedef struct class_t {
struct class_t *isa;
struct class_t *superclass;
Cache cache;
IMP *vtable;
uintptr_t data_NEVER_USE; // class_rw_t * plus flags
...
bool isRootClass() const {
return superclass == NULL;
}
bool isRootMetaclass() const {
return isa == this;
}
} class_t;
- Class structure contains a metaclass pointer(?), superclass pointer, data about the class
- data: name, ivars, methods, properties, protocols
- Superclass pointer creates the hierarchy of classes
( Categories )
- Methods, properties, and protocols define what the class can do
- stored in writable section of class definition, which can be changed at runtime
- this is how categories work (Ruby: Monkey-Patching)
- Ivars are stored in the read-only section, unmodifiable as this would impact existing instances
- thus, categories cannot change add ivars
- objc_object isa pointer is not const --> change class at runtime
- Class superclass pointer also not const --> change class hierarcy at runtime
- Now with ARC
Review
- In general: Messaging
- Method: An actual piece of code associated with a class, and given a particular name
- Message: A name and parameters sent to an object
- Selector: A particular way of representing the name of a message or method
- Message send: Taking a message and finding and executing the correct method
- SEL, IMP, method
SEL - Selector, or name of method
Method - A selector on a class
IMP - The function itself
- just a function that accepts an object pointer and selector
- It all comes from libobjc, a collection of C functions
objc_msgSend --> [object message]
Sending Messages
- What is a method?
Ex: - (int)foo:(NSString *)str { ...
Is really:
int SomeClass_method_foo_(SomeClass *self, SEL _cmd, NSString *str) { ...
Ex: int result = [obj foo:@"hello"];
Is really:
int result = ((int (*)(id, SEL, NSString *))objc_msgSend)(obj, @selector(foo:), @"hello");
(Examples from: http://mikeash.com/pyblog/friday-qa-2009-03-20-objective-c-messaging.html)
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE;
char *method_types OBJC2_UNAVAILABLE;
IMP method_imp OBJC2_UNAVAILABLE;
} OBJC2_UNAVAILABLE;
- So, a method is
- a name (selector, SEL)
- a string containing argument and return types (created by @encode)
- an IMP, or function pointer:
typedef id (*IMP)(id, SEL, ...);
<<<== MESSAGING IN DETAIL (IN KEYNOTE, STARTS HERE)
- Messaging:
objc_msgSend does the following
- look up class of given object, be dereferencing it and grabbing ISA member
- look at method list of class, search for selector
- if not found, move to superclass and do the same
- when found, jump to IMP
- There is also a method cache, to speed up this lookup process for future messages
- What about when no method found for a selector?
- Messaging (from The Objective-C Runtime Programming Guide):
- [receiver message]
objc_msgSend(receiver, selector, arg1, arg2, ...)
- dynamic binding:
- find procedure (method implementation) for selector
- call procedure, passing receiver and args
- return the procedure's return value
- isa / messaging framwork diagram p12 (Figure 3-1)
- method implementations chosen at runtime == methods dynamically bound to messages
- Hidden Arguments
- receiver and selector are called "hidden" because aren't delared in the source code
- source code can still refer to them:
- self = receiver
- _cmd = selector
- You can use methodForSelector: to bypass dynamic binding... but, it's kind of cheating since this method is provided by Cocoa.
- See Xcode project examples
- What happens in objc_msgSend?
- It's written in opcode in objc-msg-arm.s (and -i386.s, -simulator-i386.s, x86_64.s)
/********************************************************************
* id objc_msgSend(id self,
* SEL op,
* ...)
*
* On entry: a1 is the message receiver,
* a2 is the selector
********************************************************************/
ENTRY objc_msgSend
# check whether receiver is nil
teq a1, #0
itt eq
moveq a2, #0
bxeq lr
...
- Order of operations:
- Check if the receiver is nil --> nil-handler
- If garbage collection available, short-circuit on certain selectors (retain, release, autorelease, retainCount) --> return self
- Check class' cache for implementation, call it
- Compare requested selector to selectors defined in class, call it
- Compare to superclass, and up the chain
- Lazy method resolution
- Call resolveInstanceMethod: (or resolveClassMethod:), if returns YES, start over
- starts over and assumes method has been added
- Fast forwarding path
- Call forwardingTargetForSelector:, if returns non-nil, send message to object (other than self)
- starts over with new target
- Normal forwarding path
- Call methodSignatureForSelector:, if returns non-nil, create an NSInvocation and pass to forwardInvocation:
- Call doesNotRecognizeSelector: --> throws exception by default
- How to use this in a dynamic way?
- Dynamic Method Resolution:
- @dynamic synthesis of properites
- Core Data NSManagedObject does this.
- uses resolveInstanceMethod: and resolveClassMethod:
- See ch-20 Person.m/h
- Dynamic loading (not allowed in iOS)
- System Preferences modules
- NSBundle
- Fast Forwarding
- forwardingTargetForSelector: useful for proxy objects, such as CacheProxy.h/m
- See ch-20 CacheProxy.m/h (not an awesome example)
- The Higher Order Messaging (HOM) example is much more interesting
(more on this below)
- Normal Forwarding
- Slower, but more flexible
- Forwarding Failure w/ doesNotRecognizeSelector:
A (Slightly) Deep(er) Dive into objc_msgSend()
- README: http://www.friday.com/bbum/2009/12/18/objc_msgsend-part-1-the-road-map/ (all 4 parts!)
- Now we're getting crazy.
- objc_msgSend() is really a family of functions
"each written to handle the calling conventions required to deal with various return types under the x86_64 ABI (Application Binary Interface). Oh, and the vtable dispatch stuff (different post)."
- called 10s of millions of times just launching your app
- That's why it's carefully crafted in assembly.
- Show objc_msgSend() assemby just to make the point that it's written in assembly?
- Method calls in ObjC are really just C function calls to objc_msgSend(), in which it figures out C function to call
These are equivalent, generating the functionaly equivalent assembly:
- (id) doSomething: (NSUInteger) index;
id doSomething(id self, SEL _cmd, NSUInteger index) { ... }
The Objective-C Runtime Programming Guide
- Objective-C runtime is open source (opensource.apple.com)
- got objc4-493.11 here: http://opensource.apple.com/release/mac-os-x-1073/
- From the introduction:
"The Objective-C language defers as many decisions as it can from compile time and link time to runtime. Whenever possible, it does things dynamically. This means that the language requires not just a compiler, but also a runtime system to execute the compiled code. The runtime system acts as a kind of operating system for the Objective-C language; it’s what makes the language work."
"You should read this document to gain an understanding of how the Objective-C runtime system works and how you can take advantage of it. Typically, though, there should be little reason for you to need to know and understand this material to write a Cocoa application."
- Runtime interaction (from The Objective-C Runtime Programming Guide):
- Through ObjC source
- write and compile ObjC source
- NSObject methods
- allow introspection:
class, isMemberOfClass:, isKindOfClass:, respondsToSelector:, conformsToProtocol:, methodForSelector:
- direct calls to runtime functions
- Most ObjC boil down to these functions, allowing writing C code to replicate what ObjC compiles down to
- dynamic shared library with a public interface consisting of a set of functions and data structures
- Header files located in /usr/include/objc
- Message Forwarding (our concrete example?)
- forwardInvocation: (it's kind of like Ruby's method_missing)
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([someOtherObject respondsToSelector:
[anInvocation selector]])
[anInvocation invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}
- Forwarding mimics multiple inheritance
- provides features of multiple inheritance
- but, multiple inheritance combines capabilities in single object
- forwarding provides abilities in smaller objects, associated in transparent way to the message sender
- Implement "Array#map"
- HOM Example: http://www.mikeash.com/pyblog/friday-qa-2009-04-24-code-generation-with-llvm-part-2-fast-objective-c-forwarding.html
- The Complete Friday Q&A p 104
- Super not-fun to implement, here's the simpler way...
(This is old code and doesn't work... look at this just to illustrate concept)
@interface ArrayMapProxyNormal : NSProxy
{
NSArray *_array;
}
- (id)initWithArray:(NSArray *)array;
@end
@implementation ArrayMapProxyNormal
- (id)initWithArray:(NSArray *)array
{
_array = array;
return self;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
return [[_array lastObject] methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)inv
{
NSMutableArray *newArray = [NSMutableArray array];
for(id obj in _array)
{
id retval;
[inv invokeWithTarget:obj];
[inv getReturnValue:&retval;];
[newArray addObject:retval];
}
[inv setReturnValue:&newArray;];
}
@end
OnNSArray Category:
- (id)mapNormal
{
return [[[ArrayMapProxyNormal alloc] initWithArray:self] autorelease];
}
Method Swizzling
- Swizzling: transparently replacing one thing with another at runtime
in IOS, usually this is methods
- Warning: May cause App Rejection.
- Allow you to change the behaviors of Apple frameworks
- Why not use a category?
- Category method replaces original method, with no way to call original method
- Original method you wish to replace might have been implemented by category
- There is no way to determine which category method "wins"
- Option 1 (more bad): method_exhangeImplementations
- modifies selector, can break things
- pseudo-recursive call can be misleading
- Use funtion pointer approach in RNSwizzle instead
- If still interested, read this:
http://mikeash.com/pyblog/friday-qa-2010-01-29-method-replacement-for-fun-and-profit.html
- NSNotificationCenter Category Example:
#import <objc/runtime.h>
#import <objc/message.h>
@interface NSNotificationCenter (RNHijack)
+ (void)hijack;
@end
@implementation NSNotificationCenter (RNHijack)
+ (void)hijackSelector:(SEL)originalSelector withSelector:(SEL)newSelector
{
Class class = [NSNotificationCenter class];
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method overrideMethod = class_getInstanceMethod(class, newSelector);
// method_exchangeImplementations(originalMethod, overrideMethod); // Can't do this!
if (class_addMethod(c, origSEL, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod)))
{
class_replaceMethod(c, overrideSEL, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
}
else
{
method_exchangeImplementations(origMethod, overrideMethod);
}
}
+ (void)hijack
{
[self hijackSelector:@selector(removeObserver:)
withSelector:@selector(RNHijack_removeObserver:)];
}
- (void)RNHijack_removeObserver:(id)notificationObserver
{
NSLog(@"Removing observer: %@", notificationObserver);
[self RNHijack_removeObserver:notificationObserver];
}
- Option 2 (less bad): NSObject Category RNSwizzle.m/h example (ch-20)
ISA Swizzling
- ISA pointer defines the object's class
- Modifying an object's class at runtime
- NSObject Category SetClass (ISASwizzle)
- accomplishes same goal as RNSwizzle, but uses ISA swizzling, instead of method swizzling
- Makes sure to check instance size before replacing
- clobbering ISA pointer of object after this object is difficult to debug
- hence the NSAssert ensuring both Class' instances are the same size
- This is a good solution for classes that are meant to be subclassed
- KVO is implemented with ISA swizzling
- Allows frameworks to inject code into your classes
- Now you can do the reverse
Downsides to Swizzling
- why might this be bad?
- tight coupling with implementation details
- difficult to debug
- unfamiliar to others
- we're going to talk about it because it reveals the dynamic nature of Objective-C
- Rob Napier suggests using ISA over method swizzling
- only impacts specific objects, rather than all instances of a class
- use method swizzling if you actually want to affect every instance of a class
Rob Napier's talbe of Swizzling differences - P387 iOS-PTL
WHAT ABOUT...?
- KVO refresher
- Complete Friday Q&A V1: How Key-Value Observing Works
- KVO Done Right (Take 2): http://www.mikeash.com/pyblog/friday-qa-2012-03-02-key-value-observing-done-right-take-2.html
- Blocks
- Complete Friday Q&A V1:
- Practical Blocks
- Blocks in Objective-C
- Implement Clojure/Ruby collection methods with trampolining or blocks?
- Higher order messaging: http://cocoadev.com/index.pl?HigherOrderMessaging
- Complete Friday Q&A V1: Fast Objective-C Forwarding
- implement HOM: map?
Tools:
- F-Script: sort of an Objective-C REPL
- Smalltalk dialect
- http://www.fscript.org/
- syntax tutorial: http://www.fscript.org/documentation/ExploringCocoaWithFScript/index.htm
CREDITS:
- Everything:
http://iosptl.com/
The Objective-C Runtime Programming Guide
https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html
- C Basics:
http://heather.cs.ucdavis.edu/~matloff/UnixAndC/CLanguage/PointersI.html
- Messaging:
http://www.friday.com/bbum/2009/12/18/objc_msgsend-part-1-the-road-map/
http://mikeash.com/pyblog/friday-qa-2009-03-20-objective-c-messaging.html
http://mikeash.com/book.html
- Swizzling:
http://robnapier.net/blog/hijacking-methodexchangeimplementations-502