SlideShare a Scribd company logo
Objective-C Runtime
Examples	

https://github.com/storoj/objc_runtime_examples
Example 1.1
Observe value changes for all properties of a given class.
Class
Property
(objc_property_t)

getter
setter

Method
SEL

IMP
Sample class
@interface ELSample : NSObject
!
@property (nonatomic, strong) NSArray *values;
@property (nonatomic,
assign,
getter=isValid,
setter=setValidCustom:) BOOL valid;
!
@end
!
@implementation ELSample
!
@synthesize valid = ivar_valid;
!
@end
Class
Class sampleClass1 = [ELSample class];
Class sampleClass2 = NSClassFromString(@"ELSample");
#import <objc/runtime.h>
!
Class sampleClass3 = objc_getClass(“ELSample");
Class sampleClass4 = objc_lookUpClass(“ELSample");
Properties
unsigned int propertyCount = 0;
objc_property_t *properties = class_copyPropertyList(
sampleClass,
&propertyCount
);
!
for (unsigned int i=0; i<propertyCount; i++) {
objc_property_t property = properties[i];
!
const char *propertyName = property_getName(property);
NSLog(@"%s", propertyName);
}
free(properties);
Output:
values
valid
Property attributes
char const *attributesString = property_getAttributes(property);
NSLog(@"%s %s", propertyName, attributesString);
Output:
values T@"NSArray",&,N,V_values
valid Tc,N,GisValid,SsetValidCustom:,Vivar_valid

Code

Meaning

T

type (c - char, @ - id)

N

nonatomic

G

getter selector

S

setter selector

V

ivar name

&

strong/retain
More info
Property setter
/**
* @return The value string of the attribute attributeName if it exists in
* property, nil otherwise.
*/

!
char *property_copyAttributeValue(objc_property_t property,
const char *attributeName)

char *setterAttributeValue = property_copyAttributeValue(property, "S");
if (NULL == setterAttributeValue) {
"set" + capitalize(property_getName(property)) + ":"
}
// do not forget!
free(setterAttributeValue);
Method invocation
id objc_msgSend(id self, SEL _cmd, ...)
ELSample *sample = [ELSample new];
sample.valid = YES;
[sample setValidCustom:YES];
objc_msgSend(sample, @selector(setValidCustom:), YES);
id objc_msgSend(id self, SEL _cmd, ...)
{
IMP methodFunction = [[self class] methodForSelector:_cmd];
return methodFunction(self, _cmd, ...);
}
Method
Method *class_copyMethodList(Class cls, unsigned int *outCount)
!
Method class_getInstanceMethod(Class cls, SEL name)
!
Method class_getClassMethod(Class cls, SEL name)
IMP method_getImplementation(Method m)
!
/**
* @return The previous implementation of the method.
*/
IMP method_setImplementation(Method m, IMP imp)
Replacing method implementation
IMP method_setImplementation(Method m, IMP imp)
@interface Sample : NSObject
- (id)swizzleMe:(NSInteger)arg;
@end
Method method = class_getInstanceMethod(class, @selector(swizzleMe:));
typedef id (*IMP)(id, SEL, ...);
id SwizzleFunction(id self, SEL _cmd, NSInteger arg)
{
return @(arg+5);
}

!

method_setImplementation(method, (IMP)SwizzleFunction);
Blocks
id(^block)(id, id) = ^id(id self, id arg) {
NSLog(@"arg: %@", arg);
return nil;
};
IMP blockImp = imp_implementationWithBlock(block);

Block can capture original IMP to call it later.
NSString *setterName = property_getSetterName(property);
SEL setterSelector = NSSelectorFromString(setterName);
!
Method method = class_getInstanceMethod(class, setterSelector);
IMP originalImp = method_getImplementation(method);
!
id(^block)(id, ...) = ^id(id self, ...) {
NSLog(@"will change %s", property_getName(property));
return originalImp(self, setterSelector, ...);
};
!
IMP newImp = imp_implementationWithBlock(block);
!
method_setImplementation(method, newImp);

BUT…
return originalImp(self, setterSelector, ...);
(self, setterSelector, ...);
...

F*CK
Cast to concrete type
void(^block)(id, id) = ^void(id self, id arg) {
((void(*)(id, SEL, id))originalImp)(self, selector, arg);
};
// 0 - self, 1 - _cmd
char *argumentEncoding = method_copyArgumentType(method, 2);

id block = nil;
if (0 == strcmp(argumentEncoding, @encode(NSInteger))) {
block = ^void(id self, SEL selector, NSInteger arg) {
((void(*)(id, SEL, NSInteger))originalImp)(self, sel, arg);
};
} else if (...) {
...
}
Example 1.2
_objc_msgForward
id _objc_msgForward(id receiver, SEL sel, ...)
!
will call forwardInvocation: with prepared NSInvocation object

Method method = class_getInstanceMethod(class, setterSelector);
!
IMP originalImp = method_setImplementation(method, _objc_msgForward);
NSString *internalSetterName = [setterName
stringByAppendingString:@"_internal"];
SEL internalSelector = NSSelectorFromString(internalSetterName);
char const *types = method_getTypeEncoding(method);
class_addMethod(class, internalSelector, originalImp, types);
[capturedSelectorsSet addObject:setterName];
Replace forwardInvocation:
Method method = class_getInstanceMethod(class, @selector(forwardInvocation:));
IMP originalImp = method_getImplementation(method);

!

void(^block)(id, NSInvocation *) = ^void(id self, NSInvocation *invocation) {
NSString *selectorName = NSStringFromSelector([invocation selector]);

!

!

if ([capturedSelectorsSet containsObject:selectorName]) {
NSString *internalSelectorName = [selectorName
stringByAppendingString:@“_internal"];
[invocation setSelector:NSSelectorFromString(internalSelectorName)];
[invocation invoke];
} else {
((void(*)(id, SEL, NSInvocation *)) originalImp)(self,
@selector(forwardInvocation:),
invocation);
}

};

!

method_setImplementation(method, imp_implementationWithBlock(block));
Example 1.3
Replace the class
Class object_setClass(id obj, Class cls)
NSNumber *number = [NSNumber numberWithDouble:5.1f];
!
Class class = [number class];
NSLog(@"number: %@ %@ %p", number, class, (__bridge void *)number);
// number: 5.099999904632568 __NSCFNumber 0x8d464c0
!
object_setClass(number, [NSObject class]);
NSLog(@"number: %@", number);
// number: <NSObject: 0x8d464c0>
object_setClass(number, class);
NSLog(@"number: %@", number);
// number: 5.099999904632568
NSProxy
“… Subclasses of NSProxy can be used to
implement transparent distributed messaging…”

Some people, when confronted with a
problem, think "I know, I'll use NSProxy." Now
they have two problems.
@interface Observer : NSProxy
+ (instancetype)observerWithObject:(id)object;
@end

!

@implementation Observer

!

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
Class realClass = objc_getAssociatedObject(self, "realClass");
SEL selector = [anInvocation selector];
objc_property_t property = class_getPropertyWithSetter(realClass, selector);
if (NULL != property) {
NSLog(@"changing %s", property_getName(property));
}

!

}

Class currentClass = [self class];
object_setClass(self, realClass);
[anInvocation invoke];
object_setClass(self, currentClass);

!

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
Class realClass = objc_getAssociatedObject(self, "realClass");
NSMethodSignature *sig = [realClass instanceMethodSignatureForSelector:sel];
return signature;
}

!

@end
Example 2
Get outlets and actions runtime
UIRuntimeConnection
UIRuntimeOutletConnection

UIRuntimeEventConnection

UIRuntimeOutletCollectionConnection
@interface UIRuntimeConnection : NSObject <NSCoding>
!
@property (nonatomic, strong) id destination;
@property (nonatomic, strong) NSString *label;
@property (nonatomic, strong) id source;
!
- (void)connect;
- (void)connectForSimulator;
!
@end
!
@interface UIRuntimeEventConnection : UIRuntimeConnection
!
@property (nonatomic, readonly) SEL action;
@property (nonatomic, assign) UIControlEvents eventMask;
@property (nonatomic, readonly) id target;
!
@end
!
@interface UIRuntimeOutletConnection : UIRuntimeConnection
@end
kennytm
https://github.com/kennytm/iphone-private-frameworks

class-dump-z
https://code.google.com/p/networkpx/wiki/class_dump_z

runtime
Class *objc_copyClassList(unsigned int *outCount)
Protocol * __unsafe_unretained *objc_copyProtocolList(unsigned int *outCount)
Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
Method *class_copyMethodList(Class cls, unsigned int *outCount)
Protocol * __unsafe_unretained *class_copyProtocolList(Class cls,
unsigned int *outCount)
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
SEL selector = NSSelectorFromString(@"initWithCoder:");

!

Class outletClass = NSClassFromString(@"UIRuntimeOutletConnection");
Method outletMethod = class_getInstanceMethod(outletClass, selector);
method_swizzle(outletMethod, ...);

!

Class eventClass = NSClassFromString(@"UIRuntimeEventConnection");
Method eventMethod = class_getInstanceMethod(eventClass, selector);
method_swizzle(eventMethod, ...)

NO!
Why not?.. Looks good.
SEL selector = NSSelectorFromString(@"initWithCoder:");

!

Class outletClass = NSClassFromString(@"UIRuntimeOutletConnection");
Method outletMethod = class_getInstanceMethod(outletClass, selector);

!

Class eventClass = NSClassFromString(@"UIRuntimeEventConnection");
Method eventMethod = class_getInstanceMethod(eventClass, selector);

!

Class baseClass = NSClassFromString(@"UIRuntimeConnection");
Method baseMethod = class_getInstanceMethod(baseClass, selector);

!

baseMethod == outletMethod
baseMethod != eventMethod
@implementation UIRuntimeEventConnection

!

- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
// decode eventMask
}
return self;
}

!

@end
Copy method before swizzling
SEL selector = NSSelectorFromString(@"initWithCoder:");

!

Class baseClass = NSClassFromString(@"UIRuntimeConnection");
Method baseMethod = class_getInstanceMethod(baseClass, selector);

!

Class outletClass = NSClassFromString(@"UIRuntimeOutletConnection");
class_addMethod(outletClass, selector,
method_getImplementation(baseMethod),
method_getTypeEncoding(baseMethod));

!

Method outletMethod = class_getInstanceMethod(outletClass, selector);

!

method_swizzle(outletMethod, ...);
Be a man!
SEL selector = NSSelectorFromString(@"initWithCoder:");

!

Class baseClass = NSClassFromString(@"UIRuntimeConnection");
Method baseMethod = class_getInstanceMethod(baseClass, selector);

!

Class outletClass = NSClassFromString(@"UIRuntimeOutletConnection");

!

id(^initWithCoderBlock)(id, id) = ^id(id aSelf, id aDecoder) {
struct objc_super _super = {
.receiver = aSelf,
.super_class = [[aSelf class] superclass]
};

!

return objc_msgSendSuper(&_super, selector, aDecoder);

};
IMP initWithCoderImp = imp_implementationWithBlock(initWithCoderBlock);

!

class_addMethod(outletClass, selector, initWithCoderImp,
method_getTypeEncoding(baseMethod));
Output
UIStoryboardScene got ELViewController *sceneViewController
ELViewController got UIStoryboard *storyboard
UIControlEventTouchUpInside -[ELViewController buttonTap:(UIButton *)]
UIControlEventEditingChanged -[ELViewController
datePickerEditingChanged:(UIDatePicker *)]
UITableView got ELViewController *dataSource
ELViewController got UIDatePicker *datePicker
UITableView got ELViewController *delegate
ELViewController got UILabel *label
ELViewController got UIView *view
Useful links
•
•
•
•
•
•
•

Property introspection	

imp_implementationWithBlock	

class loading and initialization	

let's build objc_msgSend	

objc_msgSend ARM assembly	

private frameworks	

Objective-C Runtime Programming Guide

More Related Content

PDF
Backbone.js: Run your Application Inside The Browser
PDF
JavaScript and the AST
PDF
ES2015 workflows
PDF
FalsyValues. Dmitry Soshnikov - ECMAScript 6
PPTX
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
PDF
Powerful JavaScript Tips and Best Practices
PDF
Ten useful JavaScript tips & best practices
PPTX
Using Reflections and Automatic Code Generation
Backbone.js: Run your Application Inside The Browser
JavaScript and the AST
ES2015 workflows
FalsyValues. Dmitry Soshnikov - ECMAScript 6
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Powerful JavaScript Tips and Best Practices
Ten useful JavaScript tips & best practices
Using Reflections and Automatic Code Generation

What's hot (20)

PDF
Singletons in PHP - Why they are bad and how you can eliminate them from your...
PDF
Js 单元测试框架介绍
PDF
Design Patterns Reconsidered
KEY
Node js mongodriver
PPTX
What’s new in C# 6
PDF
Testing your javascript code with jasmine
PDF
Apache Commons - Don\'t re-invent the wheel
PPTX
The zen of async: Best practices for best performance
PDF
Spock and Geb
PDF
I os 04
ODP
ES6 PPT FOR 2016
PDF
Java libraries you can't afford to miss
PDF
Scala in practice
PDF
Fun Teaching MongoDB New Tricks
PPTX
Implementing CQRS and Event Sourcing with RavenDB
PPTX
From C++ to Objective-C
PPTX
AST - the only true tool for building JavaScript
PDF
Phactory
PDF
JavaScript Design Patterns
Singletons in PHP - Why they are bad and how you can eliminate them from your...
Js 单元测试框架介绍
Design Patterns Reconsidered
Node js mongodriver
What’s new in C# 6
Testing your javascript code with jasmine
Apache Commons - Don\'t re-invent the wheel
The zen of async: Best practices for best performance
Spock and Geb
I os 04
ES6 PPT FOR 2016
Java libraries you can't afford to miss
Scala in practice
Fun Teaching MongoDB New Tricks
Implementing CQRS and Event Sourcing with RavenDB
From C++ to Objective-C
AST - the only true tool for building JavaScript
Phactory
JavaScript Design Patterns
Ad

Similar to «Objective-C Runtime в примерах» — Алексей Сторожев, e-Legion (20)

PPTX
Objective-c Runtime
PDF
Pavel kravchenko obj c runtime
KEY
連邦の白いヤツ 「Objective-C」
PPTX
iOS Session-2
PDF
Objective c runtime
PDF
Objective-C Runtime overview
PDF
Iphone course 1
PDF
The Dark Side of Objective-C
PPT
iOS Application Development
KEY
Runtime
PDF
Никита Корчагин - Programming Apple iOS with Objective-C
PPT
Objective c intro (1)
PDF
NYU hacknight, april 6, 2016
PPTX
Binding Objective-C Libraries, Miguel de Icaza
PPT
Cocoa for Web Developers
PPTX
Presentation 1st
ODP
A quick and dirty intro to objective c
ZIP
PDF
iOS 2 - The practical Stuff
PDF
What Makes Objective C Dynamic?
Objective-c Runtime
Pavel kravchenko obj c runtime
連邦の白いヤツ 「Objective-C」
iOS Session-2
Objective c runtime
Objective-C Runtime overview
Iphone course 1
The Dark Side of Objective-C
iOS Application Development
Runtime
Никита Корчагин - Programming Apple iOS with Objective-C
Objective c intro (1)
NYU hacknight, april 6, 2016
Binding Objective-C Libraries, Miguel de Icaza
Cocoa for Web Developers
Presentation 1st
A quick and dirty intro to objective c
iOS 2 - The practical Stuff
What Makes Objective C Dynamic?
Ad

More from e-Legion (20)

PPTX
MBLT16: Elena Rydkina, Pure
PPTX
MBLT16: Alexander Lukin, AppMetrica
PPTX
MBLT16: Vincent Wu, Alibaba Mobile
PPTX
MBLT16: Dmitriy Geranin, Afisha Restorany
PPTX
MBLT16: Marvin Liao, 500Startups
PDF
MBLT16: Andrey Maslak, Aviasales
PDF
MBLT16: Andrey Bakalenko, Sberbank Online
PPTX
Rx Java architecture
PPTX
Rx java
PDF
MBLTDev15: Hector Zarate, Spotify
PDF
MBLTDev15: Cesar Valiente, Wunderlist
PDF
MBLTDev15: Brigit Lyons, Soundcloud
PDF
MBLTDev15: Egor Tolstoy, Rambler&Co
PDF
MBLTDev15: Alexander Orlov, Postforpost
PDF
MBLTDev15: Artemiy Sobolev, Parallels
PPTX
MBLTDev15: Alexander Dimchenko, DIT
PPTX
MBLTDev: Evgeny Lisovsky, Litres
PPTX
MBLTDev: Alexander Dimchenko, Bright Box
PPTX
MBLTDev15: Konstantin Goldshtein, Microsoft
PDF
MBLTDev15: Anna Mikhina, Maxim Evdokimov, Tinkoff Bank
MBLT16: Elena Rydkina, Pure
MBLT16: Alexander Lukin, AppMetrica
MBLT16: Vincent Wu, Alibaba Mobile
MBLT16: Dmitriy Geranin, Afisha Restorany
MBLT16: Marvin Liao, 500Startups
MBLT16: Andrey Maslak, Aviasales
MBLT16: Andrey Bakalenko, Sberbank Online
Rx Java architecture
Rx java
MBLTDev15: Hector Zarate, Spotify
MBLTDev15: Cesar Valiente, Wunderlist
MBLTDev15: Brigit Lyons, Soundcloud
MBLTDev15: Egor Tolstoy, Rambler&Co
MBLTDev15: Alexander Orlov, Postforpost
MBLTDev15: Artemiy Sobolev, Parallels
MBLTDev15: Alexander Dimchenko, DIT
MBLTDev: Evgeny Lisovsky, Litres
MBLTDev: Alexander Dimchenko, Bright Box
MBLTDev15: Konstantin Goldshtein, Microsoft
MBLTDev15: Anna Mikhina, Maxim Evdokimov, Tinkoff Bank

«Objective-C Runtime в примерах» — Алексей Сторожев, e-Legion

  • 2. Example 1.1 Observe value changes for all properties of a given class.
  • 4. Sample class @interface ELSample : NSObject ! @property (nonatomic, strong) NSArray *values; @property (nonatomic, assign, getter=isValid, setter=setValidCustom:) BOOL valid; ! @end ! @implementation ELSample ! @synthesize valid = ivar_valid; ! @end
  • 5. Class Class sampleClass1 = [ELSample class]; Class sampleClass2 = NSClassFromString(@"ELSample"); #import <objc/runtime.h> ! Class sampleClass3 = objc_getClass(“ELSample"); Class sampleClass4 = objc_lookUpClass(“ELSample");
  • 6. Properties unsigned int propertyCount = 0; objc_property_t *properties = class_copyPropertyList( sampleClass, &propertyCount ); ! for (unsigned int i=0; i<propertyCount; i++) { objc_property_t property = properties[i]; ! const char *propertyName = property_getName(property); NSLog(@"%s", propertyName); } free(properties); Output: values valid
  • 7. Property attributes char const *attributesString = property_getAttributes(property); NSLog(@"%s %s", propertyName, attributesString); Output: values T@"NSArray",&,N,V_values valid Tc,N,GisValid,SsetValidCustom:,Vivar_valid Code Meaning T type (c - char, @ - id) N nonatomic G getter selector S setter selector V ivar name & strong/retain More info
  • 8. Property setter /** * @return The value string of the attribute attributeName if it exists in * property, nil otherwise. */ ! char *property_copyAttributeValue(objc_property_t property, const char *attributeName) char *setterAttributeValue = property_copyAttributeValue(property, "S"); if (NULL == setterAttributeValue) { "set" + capitalize(property_getName(property)) + ":" } // do not forget! free(setterAttributeValue);
  • 9. Method invocation id objc_msgSend(id self, SEL _cmd, ...) ELSample *sample = [ELSample new]; sample.valid = YES; [sample setValidCustom:YES]; objc_msgSend(sample, @selector(setValidCustom:), YES); id objc_msgSend(id self, SEL _cmd, ...) { IMP methodFunction = [[self class] methodForSelector:_cmd]; return methodFunction(self, _cmd, ...); }
  • 10. Method Method *class_copyMethodList(Class cls, unsigned int *outCount) ! Method class_getInstanceMethod(Class cls, SEL name) ! Method class_getClassMethod(Class cls, SEL name) IMP method_getImplementation(Method m) ! /** * @return The previous implementation of the method. */ IMP method_setImplementation(Method m, IMP imp)
  • 11. Replacing method implementation IMP method_setImplementation(Method m, IMP imp) @interface Sample : NSObject - (id)swizzleMe:(NSInteger)arg; @end Method method = class_getInstanceMethod(class, @selector(swizzleMe:)); typedef id (*IMP)(id, SEL, ...); id SwizzleFunction(id self, SEL _cmd, NSInteger arg) { return @(arg+5); } ! method_setImplementation(method, (IMP)SwizzleFunction);
  • 12. Blocks id(^block)(id, id) = ^id(id self, id arg) { NSLog(@"arg: %@", arg); return nil; }; IMP blockImp = imp_implementationWithBlock(block); Block can capture original IMP to call it later.
  • 13. NSString *setterName = property_getSetterName(property); SEL setterSelector = NSSelectorFromString(setterName); ! Method method = class_getInstanceMethod(class, setterSelector); IMP originalImp = method_getImplementation(method); ! id(^block)(id, ...) = ^id(id self, ...) { NSLog(@"will change %s", property_getName(property)); return originalImp(self, setterSelector, ...); }; ! IMP newImp = imp_implementationWithBlock(block); ! method_setImplementation(method, newImp); BUT… return originalImp(self, setterSelector, ...); (self, setterSelector, ...); ... F*CK
  • 14. Cast to concrete type void(^block)(id, id) = ^void(id self, id arg) { ((void(*)(id, SEL, id))originalImp)(self, selector, arg); }; // 0 - self, 1 - _cmd char *argumentEncoding = method_copyArgumentType(method, 2); id block = nil; if (0 == strcmp(argumentEncoding, @encode(NSInteger))) { block = ^void(id self, SEL selector, NSInteger arg) { ((void(*)(id, SEL, NSInteger))originalImp)(self, sel, arg); }; } else if (...) { ... }
  • 16. _objc_msgForward id _objc_msgForward(id receiver, SEL sel, ...) ! will call forwardInvocation: with prepared NSInvocation object Method method = class_getInstanceMethod(class, setterSelector); ! IMP originalImp = method_setImplementation(method, _objc_msgForward); NSString *internalSetterName = [setterName stringByAppendingString:@"_internal"]; SEL internalSelector = NSSelectorFromString(internalSetterName); char const *types = method_getTypeEncoding(method); class_addMethod(class, internalSelector, originalImp, types); [capturedSelectorsSet addObject:setterName];
  • 17. Replace forwardInvocation: Method method = class_getInstanceMethod(class, @selector(forwardInvocation:)); IMP originalImp = method_getImplementation(method); ! void(^block)(id, NSInvocation *) = ^void(id self, NSInvocation *invocation) { NSString *selectorName = NSStringFromSelector([invocation selector]); ! ! if ([capturedSelectorsSet containsObject:selectorName]) { NSString *internalSelectorName = [selectorName stringByAppendingString:@“_internal"]; [invocation setSelector:NSSelectorFromString(internalSelectorName)]; [invocation invoke]; } else { ((void(*)(id, SEL, NSInvocation *)) originalImp)(self, @selector(forwardInvocation:), invocation); } }; ! method_setImplementation(method, imp_implementationWithBlock(block));
  • 19. Replace the class Class object_setClass(id obj, Class cls) NSNumber *number = [NSNumber numberWithDouble:5.1f]; ! Class class = [number class]; NSLog(@"number: %@ %@ %p", number, class, (__bridge void *)number); // number: 5.099999904632568 __NSCFNumber 0x8d464c0 ! object_setClass(number, [NSObject class]); NSLog(@"number: %@", number); // number: <NSObject: 0x8d464c0> object_setClass(number, class); NSLog(@"number: %@", number); // number: 5.099999904632568
  • 20. NSProxy “… Subclasses of NSProxy can be used to implement transparent distributed messaging…” Some people, when confronted with a problem, think "I know, I'll use NSProxy." Now they have two problems.
  • 21. @interface Observer : NSProxy + (instancetype)observerWithObject:(id)object; @end ! @implementation Observer ! - (void)forwardInvocation:(NSInvocation *)anInvocation { Class realClass = objc_getAssociatedObject(self, "realClass"); SEL selector = [anInvocation selector]; objc_property_t property = class_getPropertyWithSetter(realClass, selector); if (NULL != property) { NSLog(@"changing %s", property_getName(property)); } ! } Class currentClass = [self class]; object_setClass(self, realClass); [anInvocation invoke]; object_setClass(self, currentClass); ! - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { Class realClass = objc_getAssociatedObject(self, "realClass"); NSMethodSignature *sig = [realClass instanceMethodSignatureForSelector:sel]; return signature; } ! @end
  • 22. Example 2 Get outlets and actions runtime
  • 24. @interface UIRuntimeConnection : NSObject <NSCoding> ! @property (nonatomic, strong) id destination; @property (nonatomic, strong) NSString *label; @property (nonatomic, strong) id source; ! - (void)connect; - (void)connectForSimulator; ! @end ! @interface UIRuntimeEventConnection : UIRuntimeConnection ! @property (nonatomic, readonly) SEL action; @property (nonatomic, assign) UIControlEvents eventMask; @property (nonatomic, readonly) id target; ! @end ! @interface UIRuntimeOutletConnection : UIRuntimeConnection @end
  • 25. kennytm https://github.com/kennytm/iphone-private-frameworks class-dump-z https://code.google.com/p/networkpx/wiki/class_dump_z runtime Class *objc_copyClassList(unsigned int *outCount) Protocol * __unsafe_unretained *objc_copyProtocolList(unsigned int *outCount) Ivar *class_copyIvarList(Class cls, unsigned int *outCount) Method *class_copyMethodList(Class cls, unsigned int *outCount) Protocol * __unsafe_unretained *class_copyProtocolList(Class cls, unsigned int *outCount) objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
  • 26. SEL selector = NSSelectorFromString(@"initWithCoder:"); ! Class outletClass = NSClassFromString(@"UIRuntimeOutletConnection"); Method outletMethod = class_getInstanceMethod(outletClass, selector); method_swizzle(outletMethod, ...); ! Class eventClass = NSClassFromString(@"UIRuntimeEventConnection"); Method eventMethod = class_getInstanceMethod(eventClass, selector); method_swizzle(eventMethod, ...) NO! Why not?.. Looks good.
  • 27. SEL selector = NSSelectorFromString(@"initWithCoder:"); ! Class outletClass = NSClassFromString(@"UIRuntimeOutletConnection"); Method outletMethod = class_getInstanceMethod(outletClass, selector); ! Class eventClass = NSClassFromString(@"UIRuntimeEventConnection"); Method eventMethod = class_getInstanceMethod(eventClass, selector); ! Class baseClass = NSClassFromString(@"UIRuntimeConnection"); Method baseMethod = class_getInstanceMethod(baseClass, selector); ! baseMethod == outletMethod baseMethod != eventMethod @implementation UIRuntimeEventConnection ! - (id)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { // decode eventMask } return self; } ! @end
  • 28. Copy method before swizzling SEL selector = NSSelectorFromString(@"initWithCoder:"); ! Class baseClass = NSClassFromString(@"UIRuntimeConnection"); Method baseMethod = class_getInstanceMethod(baseClass, selector); ! Class outletClass = NSClassFromString(@"UIRuntimeOutletConnection"); class_addMethod(outletClass, selector, method_getImplementation(baseMethod), method_getTypeEncoding(baseMethod)); ! Method outletMethod = class_getInstanceMethod(outletClass, selector); ! method_swizzle(outletMethod, ...);
  • 29. Be a man! SEL selector = NSSelectorFromString(@"initWithCoder:"); ! Class baseClass = NSClassFromString(@"UIRuntimeConnection"); Method baseMethod = class_getInstanceMethod(baseClass, selector); ! Class outletClass = NSClassFromString(@"UIRuntimeOutletConnection"); ! id(^initWithCoderBlock)(id, id) = ^id(id aSelf, id aDecoder) { struct objc_super _super = { .receiver = aSelf, .super_class = [[aSelf class] superclass] }; ! return objc_msgSendSuper(&_super, selector, aDecoder); }; IMP initWithCoderImp = imp_implementationWithBlock(initWithCoderBlock); ! class_addMethod(outletClass, selector, initWithCoderImp, method_getTypeEncoding(baseMethod));
  • 30. Output UIStoryboardScene got ELViewController *sceneViewController ELViewController got UIStoryboard *storyboard UIControlEventTouchUpInside -[ELViewController buttonTap:(UIButton *)] UIControlEventEditingChanged -[ELViewController datePickerEditingChanged:(UIDatePicker *)] UITableView got ELViewController *dataSource ELViewController got UIDatePicker *datePicker UITableView got ELViewController *delegate ELViewController got UILabel *label ELViewController got UIView *view
  • 31. Useful links • • • • • • • Property introspection imp_implementationWithBlock class loading and initialization let's build objc_msgSend objc_msgSend ARM assembly private frameworks Objective-C Runtime Programming Guide