Let say, I have the code below
self.customObj = self.assembly.customObj() as? NSObject
let temp3 = self.assembly.customObj() as NSObject
If I use TyphoonScopeObjectGraph for customObj, it should return the same instance.
But when I debug, the customObj properties are not the same as shown:
As far as I understand, customObj and temp3 should be the same instance. But as you see in the image, customObj and temp3 have the same ObjectiveC.NSObject address but all of its properties (_shortFormatter, _longFormatter) have different address. What happen? How we can get the same instance for customObj and temp3. An example is very helpful.
Thanks.
You can get the project source code from here
In the example above if you want self.customObj and temp3 to be the same instance, then you need either TyphoonScopeSingleton or TyphoonScopeWeakSingleton.
The way TyphoonScopeObjectGraph works is that during resolution if two instances declare that they depend on another component called context, then the same shared instance of context will be returned. However these are not retained by Typhoon. So you can load a whole object graph (eg a view controller, along with dependencies) and then discard it when done, rather than use singletons, as you might otherwise. TyphoonScopeObjectGraph is also useful for having circular dependencies, such a a controller and view, that has a delegate property pointing back to the controller.
It helps to explain with an example: Let's say we have:
#interface MyViewController : UIViewController
#property(nonatomic, strong, readonly) InjectedClass(ShoppingCart) cart;
#property(nonatomic, strong) InjectedClass(MyView) view;
#end
#interface MyView : UIViewController
#property(nonatomic, strong, readonly) InjectedClass(ShoppingCart) cart;
#end
//ShoppingCart has object-graph scope
#interface ShoppingCart
#end
Now if we ask Typhoon to give us an instance of of MyViewController, then a controller will be returned where both MyViewController and MyView will have the same instance of ShoppingCart
If TyphoonScopePrototype was used, then each would have a different instance of ShoppingCart
If TyphoonScopeSingleton was used, then each would have the same instance of shopping cart, but there'd be no way to release the memory.
. . so you see TyphoonScopeObjectGraph loads an object graph with shared instances, but allows that whole object graph to be discarded after the use-case.
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
My View Controllers are holding my object properties and iVars etc. They shouldn't hold any at all.
** The rest of this post has been heavily edited for clarity as requested. **
My Class Object using the example given by NSBum that seeks to show data handled by one method, is able to continue being used in another.
#import <Foundation/Foundation.h>
#interface MYEmployee : NSObject
#property (nonatomic, strong) NSString *firstName;
#end
now for my VC interface file
#import <UIKit/UIKit.h>
#import "MYEmployee.h"
#interface MYEmployeeDetailViewController : UIViewController
#property ( nonatomic,strong) MYEmployee *employee; // as suggested
// UI elements
// a button that picks up from a textfield
- (IBAction)getName:(UIButton *)sender;
#property (strong, nonatomic) IBOutlet UITextField *inputField;
// a button that puts the names in a label
- (IBAction)showName:(UIButton *)sender;
#property (strong, nonatomic) IBOutlet UILabel *employeeNameLabel;
// a second label shows how i've been doing it so far
#property (strong, nonatomic) IBOutlet UILabel *contractorNameLabel;
#end
and the relevant essentials in my VC implementation file where i've used a private contractor property of the VC.
#import "MYEmployeeDetailViewController.h"
#interface MYEmployeeDetailViewController ()
#property (nonatomic, strong) NSString *contractor;
#end
#implementation MYEmployeeDetailViewController
// MYEmployee *employee; // use of an iVar has no effect
- (IBAction)getName:(UIButton *)sender {
// MYEmployee *employee = [[MYEmployee alloc] init]; // no effect except only within the method
// employee.firstName = self.inputField.text; //
self.employee.firstName = self.inputField.text; // class object picks up text from a textField
self.contractor = self.inputField.text; // self does exactly the same
}
- (IBAction)showName:(UIButton *)sender {
// MYEmployee *employee = [[MYEmployee alloc] init]; // resets contents of the iVar to nil
// self.employeeNameLabel.text = employee.firstName; //
self.employeeNameLabel.text = self.employee.firstName; // no joy using the property in .h file as suggested
self.contractorNameLabel.text = self.contractor; // this actually displays the text
}
#end
the employee is not to be seen while the contractor shows up for work (typical, we call them 'Blisters' they only show up when the work is done ;)
Back to serious stuff, the [alloc[init]] and iVar lines are commented out as they had no effect, but left in anyway for the sake of completeness. I didn't think I should edit the title, but my quandry is more or less the same in that "..why can't I use the model simply as a substitution of self?" After all, I outsourced the function
I might try to narrow the focus of the question somewhat; but I'll take a shot at what I think the issues are:
My View Controllers are holding my object properties and iVars etc.
They shouldn't hold any at all.
If you're following good MVC principles, then your view controllers should not have properties that would more correctly belong to the model layer. But given that view controllers are the conduit between the model and the view, certain exposed properties on the controller are essential.
Let's say you have a view controller that displays details about an employee - first name, last name, salary, department, etc. You'll probably want a model object that represents the employee. Let's call it MYEmployee. Next think about what other classes need to know about instances of MYEmployee. Those are the properties exposed in the class interface.
Now, how will our controller work with an instance of MYEmployee? Here, the view controller - let's call it MYEmployeeDetailViewController - needs to know what employee to display, so we need to expose the represented employee in the view controller's interface, e.g.
#property (nonatomic, strong) MYEmployee *employee;
Now MYEmployeeDetailViewController class users can pass an employee to display.
then how does one use Class Objects with the expectation i outlined?
I think your uncertainties revolve around questions like "who owns what data?" etc. My recommendation is to take a step back and really read the documentation on Apple's take on MVC and tap into some resources on object-oriented design. It always helps me to think first about interfaces (class interfaces, not UI) and less about implementation. Mapping out the relationships between classes before coding also helps.
You should be calling init on your new myClass object item instead of just alloc for starters. I doubt you really need to only allocate memory but not actually initialize the object.
Beyond that, your question is a bit hard to follow. You use objects to encapsulate data, and then you expose that data to other objects that might need it. Your view controllers will have objects that handle views, and objects that handle data. The controller will get what it needs from the data objects and pass it as needed to view objects. In this way, the controller will hold the data model itself (if you are doing a proper MVC design).
You say you want your class object to hold data between methods: well, that's what instance variables are for.
I am downloading a list of objects from an API to display to a user. The list has a mix of two types of objects. Imagine that they are combined books and authors, and the class definitions look like this:
#interface Book : NSObject
#property (nonatomic, strong) NSString *title;
#property (nonatomic, strong) Author *author;
#end
#interface Author : NSObject
#property (nonatomic, strong) NSString *fullName;
#property (nonatomic, weak) Book *book;
#end
Every Book can download its Author information from the API, and vice versa.
If the API gives me a Book, I can set its author property once I download it. The Author object points back to the Book through the book property, but this doesn't create an ARC Retain Cycle because the book property is weak.
However, if the API gives me an Author first, and I download its Book, the object will be deallocated once the method in which I set it returns, because the same property is weak.
I thought of a few ways around this:
Create a Content object that stores both (not viable for many-to-many relationships)
Create separate strongBook and weakBook properties, and then make a readonly property called book which checks which is set and returns that one
Those both seem messy to me, although the second option is preferable.
Is there a way to dynamically change a property from weak to strong (and vice-versa) using the Objective-C runtime?
UPDATE: I'm getting a few suggestions on how to work around the issue, which I don't have trouble coming up with myself. This question is specifically about whether there is a way to either (a) dynamically redefine #properties for a specific instance of a class, or (b) override ARC's retain/release behavior in specific circumstances (since this issue wouldn't exist in MRC).
Just a shot in the dark, but you could create the property and not specify and then use dynamic with the runtime apis. I didn't test it, but i think it should work:
//.h file
#import <Foundation/Foundation.h>
#interface SomeObject : NSObject
#property(nonatomic) NSObject *object;
#end
//.m file
#import "SomeObject.h"
#import <objc/runtime.h>
#implementation SomeObject
#dynamic object;
-(void)setObject:(NSObject *)object
{
BOOL isWeak = NO;
if(isWeak)
{
objc_setAssociatedObject(self, "object", object, OBJC_ASSOCIATION_ASSIGN);
}
else
{
objc_setAssociatedObject(self, "object", object, OBJC_ASSOCIATION_RETAIN);
}
}
-(NSObject *)object
{
return objc_getAssociatedObject(self, "object");
}
#end
For the period of the download, create a mutable dictionary to temporarily store author objects that arrive prior to the book. When a book is received, look in that array and see if the author info is there, if so attach it. When you are finished clean out the mutable array.
I'm working on a project in objective C where i have to modificate some variables which are in a view controller from uiviews.
So i've tried somethings like this :
ViewController.h :
#property (nonatomic) bool Contact;
One of the UIViews :
ViewController * View;
View.Contact = YES;
I've also tried to make a setter method like this in the ViewController :
-(void) SetterContact:(bool)boolean;
And so to modificate from a UIView like this :
[View SetterContact:YES];
But it's looking working.
I've read that i have to init the object in which is containt the variable, but in memory management it's not really good to make some initializations from object who are already actives no ?
So if View is already init, i'm not going to call the init method from another UIView no ?
Thanks for your help !
If you want bool variable to be accessible from other viewController.Then simply wirte it as :-
#property BOOL Contact;
and make an object of ViewController in which you have declared contact variable as BOOL and access this variable using like this:-
OtherViewController *otherViewController=[[OtherViewController alloc] init];
otherViewController.Contact=YES;
As it is a instance variable it has to be accessed using class object.
use #property (nonatomic, assign, getter = isContact) BOOL contact; in your .h file.
Respect naming conventions
#property (nonatomic,retain) UIViewController *myController;
don't forget to synthesize
#synthesize myController = _myController;
If you want to implement your own setter do this: respect the naming convention
-(void)setMyController:(UIViewController*)controller;
or if by any bizarre reason you can't respect naming convention you can point the property to the method you want
#property (nonatomic,retain,setter=myBizarreSetterMethod:) UIViewController *myController;
this can help you out as well question in stackoverflow
My problem is that I want to give a object back from child view (controller) to a parent view (controller). I pass the object with a call like this:
parentView.someObject=objectFromChild;
Everything is okay until the child view gets deleted (because it is poped up and no pointer shows on it) but then the object passed from child view to parent view gets also deleted. Can anyone tell me how to make it possible to save my object(even if the view which created it, is deleted)? With NSString my method works very well...but with objects I always get EXC_BAD_ACCESS
Make sure the parent object is retaining it.
Sounds like there are a couple of challenges here and I'll try to give some simple tips.
Passing Data
If you want to pass an object upward from a child to a parent, design your Child class so that the object or variable is a public property. Then, any other object (like Parent objects that own the Child) can access that property.
Keeping Data Alive
Usually EXC_BAD_ACCESS means the object has already been deleted by the system. Tell the system you want to hang on to the object by setting 'strong' in the property declaration, and this will take care of your EXC_BAD_ACCESS problem.
Take a look at the following code for an example of how to implement a very simple parent/child data relationship and retain data.
//****** Child.h
#interface Child : NSObject
// Child has a public property
// the 'strong' type qualifier will ensure it gets retained always
// It's public by default if you declare it in .h like so:
#property (strong, nonatomic) NSString *childString;
#end
//****** ParentViewController.h
#import <UIKit/UIKit.h>
#import "Child.h"
#interface ParentViewController : UIViewController
#property (strong, nonatomic) Child *myChild;
#end
//****** ParentViewController.m
#implementation ParentViewController
#synthesize myChild;
- (void)viewDidLoad {
[super viewDidLoad];
// create child object
self.myChild = [[Child alloc] init];
// set child object (from parent in this example)
// You might do your owh setting in Child's init method
self.myChild.childString = #"Hello";
// you can access your childs public property
NSLog(#"Child string = %#", self.myChild.childString;
}
Let's consider an application with highly customized or complex views.
We'll have a specific kind of view-controller sending methods to a specific kind of UIView, where the UIView is itself composed of a number of other views.
The view should have a rich, domain-specific interface, allowing the controller to act is a thin "glue" layer between it and a similarly rich model.
So we override our controller's view property as follows:
#interface PlaybackViewController : UIViewController<StageLayoutDelegate, ControlPanelDelegate>
{
NSMutableArray* _sections;
LightingMode _lightingMode;
}
#property (nonatomic, strong) PlaybackView* view; // <------ Specific type of view
#pragma mark - injected
#property (nonatomic, strong) id<OscClient> oscClient;
#property (nonatomic, strong) AbstractStageLayoutView* stageLayoutView;
#end
Ovverriding makes sense over defining another accessor, and I can just send messages to the specific type of UIView without having to cast.
Problem: The only problem is that it results in a compiler warning:
property type 'PlaybackView *' is incompatible with type 'UIView *' inherited from 'UIViewController'
. . and I like to build code that doesn't have any warnings. This way a valid warning doesn't get missed by being buried amongst other warnings.
Question:
Is there a way to suppress this particular warning?
Why is this part of the default settings, when most modern OO languages will happily allow overriding a property or method in a sub-class so that it returns a more specific sub-class of the type declared in the super-class?
The problem here is not not the override of the property, its using a forward declaration of the class type.
So this...
#class PlaybackView;
#interface PlaybackViewController : UIViewController
#property (nonatomic, strong) PlaybackView* view;
#end
will give you the mentioned warning because the compiler cannot know the inheritance hierarchy of PlaybackView. UIViewController has a contract to provide a UIView from its view property
Its telling you that it thinks PlaybackView is not a UIView
The simple solution here is to use a #import instead to give the compiler full knowledge of PlaybackView...
#import "PlaybackView.h"
#interface PlaybackViewController : UIViewController
#property (nonatomic, strong) PlaybackView* view;
#end
alternatively (but really bad form as the PCH is an optimising feature and shouldn't manage dependancies ) is to add #import "PlaybackView.h" to your projects PCH
As suggested in another answer using #import instead of #class will clear the warning but it is advised to import as little as possible in the header, so I would recommend leaving the view unchanged and having an additional PlaybackView * playbackView:
It is perfectly fine to have both view and playbackView pointing to the same view.
Classes that need to have knowledge of your specialized view have to import your controllers header, so they could just use playbackView in the first place.
More important, if you want to embed your specialized view as a subview in the future (which happens often like adding a UIScrollView superview), you won't have to refactor other code and classes!
It's plain cleaner.
I do not think override UIViewControllers view property is a good way .
I think it is better to do like this :
#interface PlaybackViewController : UIViewController<StageLayoutDelegate, ControlPanelDelegate>
{
NSMutableArray* _sections;
LightingMode _lightingMode;
}
//#property (nonatomic, strong) PlaybackView* view; //you do not need this property
#pragma mark - injected
#property (nonatomic, strong) id<OscClient> oscClient;
#property (nonatomic, strong) AbstractStageLayoutView* stageLayoutView;
#end
and in the .m file .
- (void)loadView
{
PlaybackView *mainView = [[PlaybackView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
// set the mainView
self.view = mainView;
}
and you can use your PlaybackView like this .
((PlaybackView *)(self.view)).oscClient
or
((PlaybackView *)(xxxViewControler.view)).oscClient
Perhaps you could declare another method that provides the cast for you, in a sense.
#implementation PlaybackViewController
- (void)viewDidLoad {
[super viewDidLoad];
// use view_ property instead of view
self.view_.foo = 1;
}
- (void)loadView {
CGRect frame = [UIScreen mainScreen].applicationFrame;
self.view = [[PlaybackView alloc] initWithFrame:frame];
}
- (PlaybackView *)view_ {
return (PlaybackView *)self.view;
}
Not exactly the cleanest approach, but it does avoid the cast on self.view (by not using self.view, though)
[UPDATE]
Finally I've probably found a solution that suit the problem:
This is a quick and dirty just to suppress the warning, try to wrap your code between these lines
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
//YOUR CODE
#pragma clang diagnostic pop
or -Wall
To see more about compiler warning suppression Clang Manual
[OLD ANSWER]
I'd like to give my 2 cents.
If I understood well you are trying to create a sort of Abstract factory, that gives you a specialized version of a view based on the view controller funcionality. In my opinion storyboards doesn't work well in that kind of design, but I'd like to give you my vision about it.
First I will create an abstract class for your view controller where in the interface you declare all property you need in all your VC sublcasses, such as :
OSClient
AbstractStageLayoutView
PlaybackView as weak
playbackProperty
The PlaybackView class is a class cluster such as NSNumber, you call a factory method on it, that will return an object that could be different from case to case. If you inspect an NSnumber it returns a different object if you create a float or an integer, but they are all subclasses of NSNumber, and NSNumber declares all the property of its subclasses, but it doesn't implement them.
Now what you can do in the -viewDidLoad method of the abstract class is call a method like that
PlaybackView *plbackView = [PlaybackView playbackViewFroProperty:self.playbackProperty];
[self.view addSubview:playbackView];
self.playbackView = plbackView;
The playbackProperty can be valued inside the User defined runtime attibute in the viewcontroller storyboard editor.