When my application starts I fetch a remote config file containing information (URLs, etc) required to configure other dependencies.
After I fetch the remote config I have a Config object that I need to supply to other TyphoonDefinitions.
Now I am also using the plist storyboard integration.
I was originally going down the path of injecting the assembly into the ViewController that loads the Config object, and when I receive the remote config and create the Config object, I would somehow set it as a property on the assembly. I did this hoping that I could then use the property in the definitions, but this did not work and I got:
2014-10-22 21:18:06.203 four[39840:516543] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'No component matching id 'setConfig:'.'
*** First throw call stack:
(
0 CoreFoundation 0x000000010a3e63f5 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x000000010a07fbb7 objc_exception_throw + 45
2 CoreFoundation 0x000000010a3e632d +[NSException raise:format:] + 205
3 four 0x00000001070a011d -[TyphoonComponentFactory componentForKey:args:] + 148
4 CoreFoundation 0x000000010a2de22c __invoking___ + 140
5 CoreFoundation 0x000000010a2de082 -[NSInvocation invoke] + 290
6 CoreFoundation 0x000000010a36d456 -[NSInvocation invokeWithTarget:] + 54
7 four 0x000000010709d358 -[TyphoonBlockComponentFactory forwardInvocation:] + 276
Is there any way for me to inject an object into an assembly at runtime?
Is there a cleaner way to do what I'm trying to do?
I was reading about run-time arguments which sounds like what I need, but I really don't understand the docs.
For example, I have this as a definition. I need to pass the runtime Config object as a parameter to the constructor.
- (id<ApiService>)apiService
{
return [TyphoonDefinition withClass:[ApiService class] configuration:^(TyphoonDefinition* definition) {}];
}
Using runtime arguments
For example, I have this as a definition. I need to pass the runtime
Config object as a parameter to the constructor.
- (id<ApiService>)apiService {
return [TyphoonDefinition withClass:[ApiService class] configuration:^(TyphoonDefinition* definition) {}]; }
Try something like this:
- (id<ApiService>)apiServiceWithConfig:(Config *)config {
return [TyphoonDefinition withClass:[ApiService class] configuration:^(TyphoonDefinition* definition) {
// inject into property:
[definition injectProperty:#selector(config) with:config];
// inject into constructor:
[definition useInitializer:#selector(initWithConfig:) parameters:^(TyphoonMethod *initializer) {
[initializer injectParameterWith:config];
}];
}];
}
Using factory definition
Take a look into next two definitions:
- (Config *)currentConfig {
return [TyphoonDefinition withFactory:[self configManager] selector:#selector(currentConfig)];
}
- (ConfigManager *)configManager {
return [TyphoonDefinition withClass:[ConfigManager class] configuration:^(TyphoonDefinition *definition){
definition.scope = TyphoonScopeSingleton;
}];
}
Imagine you have a ConfigManager which downloads remote config and stores it as 'currentConfig' property, 'configManager' definition describes that object.
Then check the 'currentConfig' definition. This definition just returns result of calling 'currentConfig'method on ConfigManager instance.
Then you can inject config as:
- (id<ApiService>)apiService {
return [TyphoonDefinition withClass:[ApiService class] configuration:^(TyphoonDefinition* definition) {
[definition injectProperty:#selector(config) with:[self currentConfig]];
}];
}
But make sure that currentConfig loaded (not nil) during 'apiService' creation. (maybe better inject ConfigManager instead - then if currentConfig nil, it would be filled later)
A few points:
Run-time arguments are super-cool feature and really help to get the full power of Typhoon. We strongly recommend to learn about them (ask questions here, if you like). They're not a good fit for what you're trying to do though.
It is possible to register a new definition in the container at runtime using:
.
- (void)registerDefinition:(TyphoonDefinition *)definition;
. . you probably don't want to do this in your case either.
what you are interested in is:
TyphoonComponentFactoryPostProcessor and TyphoonComponentPostProcessor:
Typhoon has two kinds of interfaces that can be attached, one is TyphoonComponentFactoryPostProcessor and the other is TyphoonComponentPostProcessor. They are used internally, but you can also write your own and do all kinds of cool things with them.
TyphoonComponentFactoryPostProcessor: Modifies the definitions (recipes) for a component before it gets built.
TyphoonComponentPostProcessor: Modifies the instances after they are built.
TyphoonConfig:
There is an existing TyphoonComponentFactoryPostProcessor that you can use for the task you described. Its called TyphoonConfigPostProcessor and is described in the user guide here.
All post processors can be attached either at built-time (in the assembly), or at runtime, as follows:
TyphoonConfigPostProcessor* configurer = [[TyphoonConfigPostProcessor alloc] init];
[configurer useResourceWithName:#"Configuration.plist"]];
[factory attachPostProcessor:configurer];
Note that, since TyphoonComponentFactoryPostProcessor modifies definitions, if you have components of singleton scope that are already built, they would not be affected. You'd have to either create lazy singletons, or call [factory unload].
How do I get a reference to the TyphoonComponentFactory after startup?
Whichever thing in your app is doing the configuration step will need to be made 'Typhoon Aware'. You can do this by using the following:
- (ConfigController *)configController
{
return [ConfigController withClass:[INFGiftDeliveryAddressController class]
configuration:^(TyphoonDefinition *definition)
{
[definition injectProperty:#selector(factory) with:self];
}];
. . when inject the assembly you can declare a property of type TyphoonComponentFactory or any of your assembly sub-classes. It will work either way. The documentation for this feature is here.
In mobile and desktop applications, your components will often need to be made 'Typhoon aware' so they we can proceed from one object graph (eg a view controller) to another (and this is where the run-time arguments feature can be useful). Note that you're coding to your own domain-specific assembly interface, so Typhoon is "non-invasive". For the most part your apps don't refer to any Typhoon APIs directly.
We've covered quite a lot here, so if you have any further questions or need clarifications don't hesitate to ask.
Related
I have a Realm object (LFEMemory) which has a publish method.
When I call the publish method, I have to upload an image to AWS and then update the object with the URL returned by Amazon.
The problem is that when the block returns from AWS, my self LFEMemory object is no longer thread-safe. (In fact, it usually is during the usual running of the app but never if I'm using an App Extension).
I could fix this by removing the publish method from the realm object, and handling it in a controller object, which can fetch a new realm object on the new thread. But that means I need to create new realms everytime I call a block, which surely isn't a good practise.
How do most people handle this situation?
- (void)publishWithBlock:(ResultBlock)block {
FileUploadManager *manager = [[FileUploadManager alloc] init];
[manager uploadWithSuccess:^(NSString *filename) {
//self is no longer thread-safe and will cause a crash
self.media.path = filename;
} failure:^(NSError *error) {
block(NO, error);
};
}
You have various options you can explore:
1) if your object has a primary key (string or a number) you can store the id as a constant inside the method and use it to fetch back the object from any thread by using [Realm objectOfType:forPrimaryKey:]. docs
Don't be afraid to get a new realm from the different thread if that's what you need to do - that does not create "another" Realm or duplicate your file.
2) if you don't have a primary key you can simply create self.media on the main thread and whenever the upload has finished, switch again to the main queue and modify your object there - modifying a property or two on a Realm object will not harm at all your performance on the main thread.
Further - if you have access to your object (as in self.media) it already gives you access to the original Realm used to create / read the object via its realm property docs
Still - I'd go with using the primary key of the object to re-fetch a reference to the object I need if in doubt.
In OCMockito, test doubles are implemented with NSProxy. A double standing in for an instance implements -respondsToSelector: as follows:
- (BOOL)respondsToSelector:(SEL)aSelector {
return [_mockedClass instancesRespondToSelector:aSelector];
}
But a double standing in for a class implements -respondsToSelector: like this:
- (BOOL)respondsToSelector:(SEL)aSelector {
return [_mockedClass respondsToSelector:aSelector];
}
This all works in the 32-bit runtime. For example, if _mockedClass is [NSString class], the proxy correctly answers that it responds to the selector +pathWithComponents:
But in the 64-bit runtime, it crashes:
Crashed Thread: 0 Dispatch queue: com.apple.main-thread
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: EXC_I386_GPFLT
Application Specific Information:
objc[1868]: GC: forcing GC OFF because OBJC_DISABLE_GC is set
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 libobjc.A.dylib 0x00007fff95cbffc6 cache_getImp + 6
1 libobjc.A.dylib 0x00007fff95ccd1dc lookUpImpOrForward + 50
2 libobjc.A.dylib 0x00007fff95ccd198 lookUpImpOrNil + 20
3 libobjc.A.dylib 0x00007fff95cc218a class_respondsToSelector + 37
4 com.apple.CoreFoundation 0x00007fff91c131ad ___forwarding___ + 429
5 com.apple.CoreFoundation 0x00007fff91c12f78 _CF_forwarding_prep_0 + 120
6 org.mockito.OCMockitoTests 0x000000010451a55b -[StubClassTest testStubbedMethod_ShouldReturnGivenObject] + 107 (StubClassTest.m:48)
Note that it's calling class_respondsToSelector(…). I suspect that I'm being bitten by an optimization made to the runtime. What can I do to fix this?
it's a bit long answer, so bear with me. I ran a simple code just to verify the behavior:
Class mock = mockClass([NSProcessInfo class]);
[mock processInfo];
[verify(mock) processInfo];
Indeed It does crash with bad pointer exception. Replacing first line with
id mock = mockClass([NSProcessInfo class]);
works as expected. I figured that it might be worth to look at the code after ARC. Those snippets are a bit to long, so here are the gists: Class-based test, id-based test
As you can see, when you declare variable of type Class there is an extra release. My guess is that since classes are registered for the entire runtime duration (unless removed using runtime api) it's ok to have Class variable as __unsafe_unretained.
To summarize, you have two possible solutions:
#implementation StubClassTest
{
__strong Class mockClass;
}
or
#implementation StubClassTest
{
id mockClass;
}
seem to fix the issue for me.
Update
As a special case, if the object’s base type is Class (possibly protocol-qualified), the type is adjusted to have __unsafe_unretained qualification instead.
From http://clang.llvm.org/docs/AutomaticReferenceCounting.html#objects
I have an AXObserver registered like so:
AXObserverCreate(pid, axObserverCallback, &_observer);
This is the callback method:
static void axObserverCallback(AXObserverRef observer, AXUIElementRef elementRef, CFStringRef notification, void *self) { .. }
And this is how I add a notification:
AXObserverAddNotification(_observer, app, kAXFocusedWindowChangedNotification, self);
What I'm noticing is that the element added to the observer (for notifying) (in the above case "app") does not seem to correspond to the one set in the observer callback ("elementRef"). It looks like it's a copy? At least I can't compare them with == and if I NSLog them they show a different address.
Is there any way to compare AXUIElementRefs that are copies? (And is this the normal behavior to return a copy instead of the original?)
Like all Core Foundation objects, AXUIElementRefs should be compared using CFEqual(), not by checking pointer equality.
The Daemons and Services Programming Guides tells that it is possible to return a proxy object through an open XPC connection, even as a reply block parameter.
Passing an Object By Proxy
Most of the time, it makes sense to copy objects and send them to the other side of a connection. However, this is not always desirable. In particular:
If you need to share a single instance of the data between the client application and the helper, you must pass the objects by proxy.
If an object needs to call methods on other objects within your application that you cannot or do not wish to pass across the connection (such as user interface objects), then you must pass an object by proxy—either the caller, the callee (where possible), or a relay object that you construct specifically for that purpose.
The downside to passing objects by proxy is that performance is significantly reduced (because every access to the object requires interprocess communication). For this reason, you should only pass objects by proxy if it is not possible to pass them by copying.
You can configure additional proxy objects similarly to the way you configured the remoteObjectInterface property of the initial connection. First, identify which parameter to a method should be passed by proxy, then specify an NSXPCInterface object that defines the interface for that object.
First questions come: how should the object to be passed by proxy be defined? As an object conforming to NSXPCProxyCreating protocol? Should remoteObjectProxy and remoteObjectProxyWithErrorHandler: method be implemented then?
An example follows, that is not clear at all to me. In particular I don't understand where should I call the NSXPCInterface method (setInterface:forSelector:argumentIndex:ofReply:) to whitelist the parameter as a proxy: in the XPC service code or in the host?
The first parameter to a method is parameter 0, followed by parameter 1, and so on.
In this case, the value NO is passed for the ofReply parameter because this code is modifying the whitelist for one of the parameters of the method itself. If you are whitelisting a class for a parameter of the method’s reply block, pass YES instead.
So the question is: can anybody provide me with a clear tutorial on how to return an object as a proxy in a block reply of a XPC method call?
I can answer my own question now: to return an object as a proxy in a block reply of a XPC method call, one should call the setInterface:forSelector:argumentIndex:ofReply: method both:
in the XPC service's endpoint, where the exportedInterface is declared
in the host, where the remoteObjectInterface is declared
I.e, common code:
// common (service/host) protocol definition
#protocol Service
#end
#protocol ServiceFactory
-(void)connectToNewService: (void (^)(id<Service>)reply;
#end
In the XPC Service:
// Implement the one method in the NSXPCListenerDelegate protocol.
-(BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection*)newConnection {
NSXPCInterface *serviceFactoryInterface =[NSXPCInterface interfaceWithProtocol:#protocol(ServiceFactory)];
NSXPCInterface *serviceInterface =[NSXPCInterface interfaceWithProtocol:#protocol(Service)];
// connection has to be returned as proxy, not as a copy
[serviceFactoryInterface setInterface: serviceInterface
forSelector: #selector(connectToNewService:)
argumentIndex: 0
ofReply: YES];
newConnection.exportedInterface = serviceFactoryInterface;
newConnection.exportedObject = self;
[newConnection resume];
return YES;
}
In the host code:
// in the host
- (void)openNewService
{
NSXPCConnection *xpcConnection = [[NSXPCConnection alloc] initWithServiceName:#"eu.mycompany.servicefactory"];
NSXPCInterface *serviceFactoryInterface =[NSXPCInterface interfaceWithProtocol:#protocol(ServiceFactory)];
NSXPCInterface *serviceInterface =[NSXPCInterface interfaceWithProtocol:#protocol(Service)];
// connection has to be returned as proxy, not as a copy
[serviceFactoryInterface setInterface: serviceInterface
forSelector: #selector(connectToNewService:)
argumentIndex: 0
ofReply: YES];
xpcConnection.remoteObjectInterface = serviceFactoryInterface;
[xpcConnection resume];
[[xpcConnection remoteObjectProxy] connectToNewService:^(id<Service> newService) {
// here a newService is returned as NSXPCDistantObject <Service>*
[xpcConnection invalidate];
}];
}
I have a setter method (setMinimumNumberOfSides) that I want to override after using synthesize. In it, I'm putting in a constraint on the instance variable to make sure the int is within certain bounds.
Later in a custom init method, I'm setting another instance variable (numberOfSides), but I need to make sure minimumNumberOfSides and maximumNumberOfSides was set properly within bounds. I tried changing the return value on the setter to a BOOL, so I could pass back a YES or NO if it succeeded/failed, but that created a conflicting method, I'm guessing because I'm using synthesize and overriding the setter.
How can I get the info out easily to check to see if the setter was called and returned successfully?
-(void)setNumberOfSides:(int)sides
{
if ((sides < maximumNumberOfSides) && (sides > minimumNumberOfSides))
{
numberOfSides = sides;
}
else
NSLog (#"Invalid number of sides: %d is outside the constraints allowed", sides);
}
-(void)setMinimumNumberOfSides:(int)minimum
{
if (minimum > 2)
minimumNumberOfSides = minimum;
}
-(void)setMaximumNumberOfSides:(int)maximum
{
if (maximum <= 12)
maximumNumberOfSides = maximum;
}
-(id)initWithNumberOfSides:(int)sides minimumNumberOfSides:(int)min maximumNumberOfSides:(int)max
{
if (self = [super init])
{
self.minimumNumberOfSides = min;
self.maximumNumberOfSides = max;
self.numberOfSides = sides;
}
return self;
}
You don't have to synthesize numberOfSides if you're planning on implementing the getter and setter. Without #synthesize numberOfSides you can return a BOOL if you choose. You'll need to declare the getter/setter in your interface accordingly.
BTW, another approach would be to use the synthesized getter/setter and add a separate method -(BOOL)isNumberOfSidesValid which performs this check.
In a situation like this, you may be better off using a simple call to assert(), or throwing an exception.
The choice will depend on how you see this class being used. If it will be part of a library, and you expect other developers to frequently supply incorrect values for minimumNumberOfSides or maximumNumberOfSides, you should probably throw a proper exception.
A word of warning, though. If you expect the users of your application to frequently supply incorrect values, then an exception is a bad idea. Exception handling in Objective-C is an expensive operation. If these checks are in place for the sake of the user, you should perform input validation, and report errors to the user in a much more friendly manner.
edit: Here is some quick sample code:
-(void)setMinimumNumberOfSides:(int)minimum
{
if (minimum <= 2)
{
[NSException raise:#"invalid minimumNumberOfSides value"
format:#"value of %d is too low (must be > 2)", minimum];
}
minimumNumberOfSides = minimum;
}
edit: Here is another SO question that goes into detail about exception handling in Objective-C.