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

What's hot

Singletons in PHP - Why they are bad and how you can eliminate them from your...
Singletons in PHP - Why they are bad and how you can eliminate them from your...Singletons in PHP - Why they are bad and how you can eliminate them from your...
Singletons in PHP - Why they are bad and how you can eliminate them from your...
go_oh
 
Js 单元测试框架介绍
Js 单元测试框架介绍Js 单元测试框架介绍
Js 单元测试框架介绍louieuser
 
Design Patterns Reconsidered
Design Patterns ReconsideredDesign Patterns Reconsidered
Design Patterns Reconsidered
Alex Miller
 
Node js mongodriver
Node js mongodriverNode js mongodriver
Node js mongodriver
christkv
 
What’s new in C# 6
What’s new in C# 6What’s new in C# 6
What’s new in C# 6
Fiyaz Hasan
 
Testing your javascript code with jasmine
Testing your javascript code with jasmineTesting your javascript code with jasmine
Testing your javascript code with jasmineRubyc Slides
 
Apache Commons - Don\'t re-invent the wheel
Apache Commons - Don\'t re-invent the wheelApache Commons - Don\'t re-invent the wheel
Apache Commons - Don\'t re-invent the wheel
tcurdt
 
The zen of async: Best practices for best performance
The zen of async: Best practices for best performanceThe zen of async: Best practices for best performance
The zen of async: Best practices for best performance
Microsoft Developer Network (MSDN) - Belgium and Luxembourg
 
ES6 PPT FOR 2016
ES6 PPT FOR 2016ES6 PPT FOR 2016
ES6 PPT FOR 2016
Manoj Kumar
 
Java libraries you can't afford to miss
Java libraries you can't afford to missJava libraries you can't afford to miss
Java libraries you can't afford to miss
Andres Almiray
 
Scala in practice
Scala in practiceScala in practice
Scala in practice
andyrobinson8
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New Tricks
MongoDB
 
Implementing CQRS and Event Sourcing with RavenDB
Implementing CQRS and Event Sourcing with RavenDBImplementing CQRS and Event Sourcing with RavenDB
Implementing CQRS and Event Sourcing with RavenDB
Oren Eini
 
From C++ to Objective-C
From C++ to Objective-CFrom C++ to Objective-C
From C++ to Objective-C
corehard_by
 
AST - the only true tool for building JavaScript
AST - the only true tool for building JavaScriptAST - the only true tool for building JavaScript
AST - the only true tool for building JavaScriptIngvar Stepanyan
 
JavaScript Design Patterns
JavaScript Design PatternsJavaScript Design Patterns
JavaScript Design Patterns
Derek Brown
 

What's hot (20)

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

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

Objective-c Runtime
Objective-c RuntimeObjective-c Runtime
Objective-c Runtime
Pavel Albitsky
 
Agile Iphone Development
Agile Iphone DevelopmentAgile Iphone Development
Agile Iphone Development
Giordano Scalzo
 
連邦の白いヤツ 「Objective-C」
連邦の白いヤツ 「Objective-C」連邦の白いヤツ 「Objective-C」
連邦の白いヤツ 「Objective-C」
matuura_core
 
Groovy Ast Transformations (greach)
Groovy Ast Transformations (greach)Groovy Ast Transformations (greach)
Groovy Ast Transformations (greach)
HamletDRC
 
Ast transformations
Ast transformationsAst transformations
Ast transformations
HamletDRC
 
Functional Core, Reactive Shell
Functional Core, Reactive ShellFunctional Core, Reactive Shell
Functional Core, Reactive Shell
Giovanni Lodi
 
Pavel kravchenko obj c runtime
Pavel kravchenko obj c runtimePavel kravchenko obj c runtime
Pavel kravchenko obj c runtimeDneprCiklumEvents
 
AST Transformations
AST TransformationsAST Transformations
AST TransformationsHamletDRC
 
JavaScript (without DOM)
JavaScript (without DOM)JavaScript (without DOM)
JavaScript (without DOM)Piyush Katariya
 
Swift와 Objective-C를 함께 쓰는 방법
Swift와 Objective-C를 함께 쓰는 방법Swift와 Objective-C를 함께 쓰는 방법
Swift와 Objective-C를 함께 쓰는 방법
Jung Kim
 
PYTHON-Chapter 3-Classes and Object-oriented Programming: MAULIK BORSANIYA
PYTHON-Chapter 3-Classes and Object-oriented Programming: MAULIK BORSANIYAPYTHON-Chapter 3-Classes and Object-oriented Programming: MAULIK BORSANIYA
PYTHON-Chapter 3-Classes and Object-oriented Programming: MAULIK BORSANIYA
Maulik Borsaniya
 
srcArtifact.javasrcArtifact.javaclassArtifactextendsCave{pub.docx
srcArtifact.javasrcArtifact.javaclassArtifactextendsCave{pub.docxsrcArtifact.javasrcArtifact.javaclassArtifactextendsCave{pub.docx
srcArtifact.javasrcArtifact.javaclassArtifactextendsCave{pub.docx
whitneyleman54422
 
11. session 11 functions and objects
11. session 11   functions and objects11. session 11   functions and objects
11. session 11 functions and objectsPhúc Đỗ
 
Metaprogramming in JavaScript
Metaprogramming in JavaScriptMetaprogramming in JavaScript
Metaprogramming in JavaScript
Mehdi Valikhani
 
Object Oriented JavaScript
Object Oriented JavaScriptObject Oriented JavaScript
Object Oriented JavaScript
Julie Iskander
 

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

Objective-c Runtime
Objective-c RuntimeObjective-c Runtime
Objective-c Runtime
 
Runtime
RuntimeRuntime
Runtime
 
Agile Iphone Development
Agile Iphone DevelopmentAgile Iphone Development
Agile Iphone Development
 
連邦の白いヤツ 「Objective-C」
連邦の白いヤツ 「Objective-C」連邦の白いヤツ 「Objective-C」
連邦の白いヤツ 「Objective-C」
 
Prototype Framework
Prototype FrameworkPrototype Framework
Prototype Framework
 
Groovy Ast Transformations (greach)
Groovy Ast Transformations (greach)Groovy Ast Transformations (greach)
Groovy Ast Transformations (greach)
 
Java Script Workshop
Java Script WorkshopJava Script Workshop
Java Script Workshop
 
Ast transformations
Ast transformationsAst transformations
Ast transformations
 
Functional Core, Reactive Shell
Functional Core, Reactive ShellFunctional Core, Reactive Shell
Functional Core, Reactive Shell
 
Pavel kravchenko obj c runtime
Pavel kravchenko obj c runtimePavel kravchenko obj c runtime
Pavel kravchenko obj c runtime
 
AST Transformations
AST TransformationsAST Transformations
AST Transformations
 
JavaScript (without DOM)
JavaScript (without DOM)JavaScript (without DOM)
JavaScript (without DOM)
 
iOS Session-2
iOS Session-2iOS Session-2
iOS Session-2
 
Swift와 Objective-C를 함께 쓰는 방법
Swift와 Objective-C를 함께 쓰는 방법Swift와 Objective-C를 함께 쓰는 방법
Swift와 Objective-C를 함께 쓰는 방법
 
PYTHON-Chapter 3-Classes and Object-oriented Programming: MAULIK BORSANIYA
PYTHON-Chapter 3-Classes and Object-oriented Programming: MAULIK BORSANIYAPYTHON-Chapter 3-Classes and Object-oriented Programming: MAULIK BORSANIYA
PYTHON-Chapter 3-Classes and Object-oriented Programming: MAULIK BORSANIYA
 
srcArtifact.javasrcArtifact.javaclassArtifactextendsCave{pub.docx
srcArtifact.javasrcArtifact.javaclassArtifactextendsCave{pub.docxsrcArtifact.javasrcArtifact.javaclassArtifactextendsCave{pub.docx
srcArtifact.javasrcArtifact.javaclassArtifactextendsCave{pub.docx
 
11. session 11 functions and objects
11. session 11   functions and objects11. session 11   functions and objects
11. session 11 functions and objects
 
Metaprogramming in JavaScript
Metaprogramming in JavaScriptMetaprogramming in JavaScript
Metaprogramming in JavaScript
 
Object Oriented JavaScript
Object Oriented JavaScriptObject Oriented JavaScript
Object Oriented JavaScript
 
Javascript
JavascriptJavascript
Javascript
 

More from e-Legion

MBLT16: Elena Rydkina, Pure
MBLT16: Elena Rydkina, PureMBLT16: Elena Rydkina, Pure
MBLT16: Elena Rydkina, Pure
e-Legion
 
MBLT16: Alexander Lukin, AppMetrica
MBLT16: Alexander Lukin, AppMetricaMBLT16: Alexander Lukin, AppMetrica
MBLT16: Alexander Lukin, AppMetrica
e-Legion
 
MBLT16: Vincent Wu, Alibaba Mobile
MBLT16: Vincent Wu, Alibaba MobileMBLT16: Vincent Wu, Alibaba Mobile
MBLT16: Vincent Wu, Alibaba Mobile
e-Legion
 
MBLT16: Dmitriy Geranin, Afisha Restorany
MBLT16: Dmitriy Geranin, Afisha RestoranyMBLT16: Dmitriy Geranin, Afisha Restorany
MBLT16: Dmitriy Geranin, Afisha Restorany
e-Legion
 
MBLT16: Marvin Liao, 500Startups
MBLT16: Marvin Liao, 500StartupsMBLT16: Marvin Liao, 500Startups
MBLT16: Marvin Liao, 500Startups
e-Legion
 
MBLT16: Andrey Maslak, Aviasales
MBLT16: Andrey Maslak, AviasalesMBLT16: Andrey Maslak, Aviasales
MBLT16: Andrey Maslak, Aviasales
e-Legion
 
MBLT16: Andrey Bakalenko, Sberbank Online
MBLT16: Andrey Bakalenko, Sberbank OnlineMBLT16: Andrey Bakalenko, Sberbank Online
MBLT16: Andrey Bakalenko, Sberbank Online
e-Legion
 
Rx Java architecture
Rx Java architectureRx Java architecture
Rx Java architecture
e-Legion
 
Rx java
Rx javaRx java
Rx java
e-Legion
 
MBLTDev15: Hector Zarate, Spotify
MBLTDev15: Hector Zarate, SpotifyMBLTDev15: Hector Zarate, Spotify
MBLTDev15: Hector Zarate, Spotify
e-Legion
 
MBLTDev15: Cesar Valiente, Wunderlist
MBLTDev15: Cesar Valiente, WunderlistMBLTDev15: Cesar Valiente, Wunderlist
MBLTDev15: Cesar Valiente, Wunderlist
e-Legion
 
MBLTDev15: Brigit Lyons, Soundcloud
MBLTDev15: Brigit Lyons, SoundcloudMBLTDev15: Brigit Lyons, Soundcloud
MBLTDev15: Brigit Lyons, Soundcloud
e-Legion
 
MBLTDev15: Egor Tolstoy, Rambler&Co
MBLTDev15: Egor Tolstoy, Rambler&CoMBLTDev15: Egor Tolstoy, Rambler&Co
MBLTDev15: Egor Tolstoy, Rambler&Co
e-Legion
 
MBLTDev15: Alexander Orlov, Postforpost
MBLTDev15: Alexander Orlov, PostforpostMBLTDev15: Alexander Orlov, Postforpost
MBLTDev15: Alexander Orlov, Postforpost
e-Legion
 
MBLTDev15: Artemiy Sobolev, Parallels
MBLTDev15: Artemiy Sobolev, ParallelsMBLTDev15: Artemiy Sobolev, Parallels
MBLTDev15: Artemiy Sobolev, Parallels
e-Legion
 
MBLTDev15: Alexander Dimchenko, DIT
MBLTDev15: Alexander Dimchenko, DITMBLTDev15: Alexander Dimchenko, DIT
MBLTDev15: Alexander Dimchenko, DIT
e-Legion
 
MBLTDev: Evgeny Lisovsky, Litres
MBLTDev: Evgeny Lisovsky, LitresMBLTDev: Evgeny Lisovsky, Litres
MBLTDev: Evgeny Lisovsky, Litres
e-Legion
 
MBLTDev: Alexander Dimchenko, Bright Box
MBLTDev: Alexander Dimchenko, Bright Box MBLTDev: Alexander Dimchenko, Bright Box
MBLTDev: Alexander Dimchenko, Bright Box
e-Legion
 
MBLTDev15: Konstantin Goldshtein, Microsoft
MBLTDev15: Konstantin Goldshtein, MicrosoftMBLTDev15: Konstantin Goldshtein, Microsoft
MBLTDev15: Konstantin Goldshtein, Microsoft
e-Legion
 
MBLTDev15: Anna Mikhina, Maxim Evdokimov, Tinkoff Bank
MBLTDev15: Anna Mikhina, Maxim Evdokimov, Tinkoff Bank MBLTDev15: Anna Mikhina, Maxim Evdokimov, Tinkoff Bank
MBLTDev15: Anna Mikhina, Maxim Evdokimov, Tinkoff Bank
e-Legion
 

More from e-Legion (20)

MBLT16: Elena Rydkina, Pure
MBLT16: Elena Rydkina, PureMBLT16: Elena Rydkina, Pure
MBLT16: Elena Rydkina, Pure
 
MBLT16: Alexander Lukin, AppMetrica
MBLT16: Alexander Lukin, AppMetricaMBLT16: Alexander Lukin, AppMetrica
MBLT16: Alexander Lukin, AppMetrica
 
MBLT16: Vincent Wu, Alibaba Mobile
MBLT16: Vincent Wu, Alibaba MobileMBLT16: Vincent Wu, Alibaba Mobile
MBLT16: Vincent Wu, Alibaba Mobile
 
MBLT16: Dmitriy Geranin, Afisha Restorany
MBLT16: Dmitriy Geranin, Afisha RestoranyMBLT16: Dmitriy Geranin, Afisha Restorany
MBLT16: Dmitriy Geranin, Afisha Restorany
 
MBLT16: Marvin Liao, 500Startups
MBLT16: Marvin Liao, 500StartupsMBLT16: Marvin Liao, 500Startups
MBLT16: Marvin Liao, 500Startups
 
MBLT16: Andrey Maslak, Aviasales
MBLT16: Andrey Maslak, AviasalesMBLT16: Andrey Maslak, Aviasales
MBLT16: Andrey Maslak, Aviasales
 
MBLT16: Andrey Bakalenko, Sberbank Online
MBLT16: Andrey Bakalenko, Sberbank OnlineMBLT16: Andrey Bakalenko, Sberbank Online
MBLT16: Andrey Bakalenko, Sberbank Online
 
Rx Java architecture
Rx Java architectureRx Java architecture
Rx Java architecture
 
Rx java
Rx javaRx java
Rx java
 
MBLTDev15: Hector Zarate, Spotify
MBLTDev15: Hector Zarate, SpotifyMBLTDev15: Hector Zarate, Spotify
MBLTDev15: Hector Zarate, Spotify
 
MBLTDev15: Cesar Valiente, Wunderlist
MBLTDev15: Cesar Valiente, WunderlistMBLTDev15: Cesar Valiente, Wunderlist
MBLTDev15: Cesar Valiente, Wunderlist
 
MBLTDev15: Brigit Lyons, Soundcloud
MBLTDev15: Brigit Lyons, SoundcloudMBLTDev15: Brigit Lyons, Soundcloud
MBLTDev15: Brigit Lyons, Soundcloud
 
MBLTDev15: Egor Tolstoy, Rambler&Co
MBLTDev15: Egor Tolstoy, Rambler&CoMBLTDev15: Egor Tolstoy, Rambler&Co
MBLTDev15: Egor Tolstoy, Rambler&Co
 
MBLTDev15: Alexander Orlov, Postforpost
MBLTDev15: Alexander Orlov, PostforpostMBLTDev15: Alexander Orlov, Postforpost
MBLTDev15: Alexander Orlov, Postforpost
 
MBLTDev15: Artemiy Sobolev, Parallels
MBLTDev15: Artemiy Sobolev, ParallelsMBLTDev15: Artemiy Sobolev, Parallels
MBLTDev15: Artemiy Sobolev, Parallels
 
MBLTDev15: Alexander Dimchenko, DIT
MBLTDev15: Alexander Dimchenko, DITMBLTDev15: Alexander Dimchenko, DIT
MBLTDev15: Alexander Dimchenko, DIT
 
MBLTDev: Evgeny Lisovsky, Litres
MBLTDev: Evgeny Lisovsky, LitresMBLTDev: Evgeny Lisovsky, Litres
MBLTDev: Evgeny Lisovsky, Litres
 
MBLTDev: Alexander Dimchenko, Bright Box
MBLTDev: Alexander Dimchenko, Bright Box MBLTDev: Alexander Dimchenko, Bright Box
MBLTDev: Alexander Dimchenko, Bright Box
 
MBLTDev15: Konstantin Goldshtein, Microsoft
MBLTDev15: Konstantin Goldshtein, MicrosoftMBLTDev15: Konstantin Goldshtein, Microsoft
MBLTDev15: Konstantin Goldshtein, Microsoft
 
MBLTDev15: Anna Mikhina, Maxim Evdokimov, Tinkoff Bank
MBLTDev15: Anna Mikhina, Maxim Evdokimov, Tinkoff Bank MBLTDev15: Anna Mikhina, Maxim Evdokimov, Tinkoff Bank
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