Im struggling to solve in a very clean way a problematic involving memory overload (management).
Im having a serie of view that include other views, in my project I have a situation like this:
MainView
|_PageView
|_CustomButton
soo far soo good, easy as a cake. CustomButton have a delegate (protocol) in it for some reasons, so we have in PageView a "for cycle" that creates N CustomButtons, set the delegate as self in PageView (PageVew extend CustomButtonDelegate) and release the buttons afer attaching them like
{
CustomButton *customButton_ = [[CustomButton alloc] initWithFrame:CGRectMake(100.0,50+(i*55.0),200.0);
customButton.delegate = self;
[self addSubView:customButton_];
[customButton_ release];
}
soo far soo good again. Button will be press, PageView get the protocol method, do some code and voilĂ , done. One problem is that at one point, MainView must remove PageView, so In a method I call
[pageView_ removeFromSuperview];
[pageView release], pageView_ = nil;
pageView_ = [PageView alloc] initWithFrame.....];
and I recreate the object with other data to display.
I noticed that PageView never gets release and removed from the memory because its retainCount is exactly how many CustomButton I created inside PageView and assign the delegate to self plus one of course. My question is, what is the cleanest way to remove safely all the objects and be able to remove PageView too, free the memory (because Im loading a quite large amount of data to display in it) ?
Right now i'm doing:
Create in PageView a NSMutableArray, that I CustomButton the objects in
it, and before to remove PageView, I cycle it and set the delegate = nil and then release
each object, after I release the NSMutableArray (called "holder").
But the problem is that if I want to add more objects of different types with other protocols, adding to this array, can lead to other problems of retaining the objects.
Where do I lack guys, knowledge so I need to study more (quite sure I can say) or do I need to approach with another OOD?
Thank you guys, im going overload with this problem and my brain is stuck in a close road. :)
Looks like your CustomButton's delegate is a retain property of CustomButton. Delegate should be an assign property, not retain nor copy. See here.
Related
My app has a search view(search bar) which is used all over the app. I don't want to create duplicated code so I created a view controller called MySearchViewController to handle the search job, then I created a singleton object in AppDelegate. In every view controller, I added my search view like this:
- (void)viewDidLoad
{
MySearchViewController* search = [AppDelegate searchViewController];
[self.view addSubView:search.view];
}
My questions, Is it a good way? It's a singleton so it can be added to many views. Do I need to remove the view from last view before adding to current view?
Understand that you are mixing some concepts that are not necessarily related: avoid duplicated code and Singletons.
Wikipedia says this about singletons:
In software engineering, the singleton pattern is a design pattern that restricts the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system. The concept is sometimes generalized to systems that operate more efficiently when only one object exists, or that restrict the instantiation to a certain number of objects. The term comes from the mathematical concept of a singleton.
The most important characteristic of a singleton (in my humble opinion) is that the object is instantiated only once and every single place in your application will use the same instance. Well, to use your search feature everywhere and avoid duplicated code you don't need the search view to be instantiated only once, maybe the data that comes with it, but not the view itself.
Two better ways of achieving this:
1 - You can create a ViewController with your search and just embed this on the other views using a Container View, you can use blocks or a delegate protocol to communicate between your controller and the view that is embedding it.
2 - You can create a Parent class of the ViewController that will include the search bar, like a SearchViewController and all the other viewControllers that needs the same feature will inherit from it.
The singleton could be useful if you are planing to share the same search data and text between all the ViewControllers of the application, but it would be a singleton only with these information, the UISearchBar and all other view elements should not be part of the singleton.
Ideally, you should instantiate a fresh instance of MySearchViewController every time when you want to add it to another view to avoid problems.
Do I need to remove the view from last view before adding to current view?
Its not required to remove it from previous super view because whenever you add this singleton MySearchViewController's view to some other view, it will automatically gets removed from last super view and now its super view is your new view where you have added it.
If you want to add a view from a different view controller, your view controller has to be that view controller's parent view controller:
- (void)viewDidLoad
{
MySearchViewController* search = [AppDelegate searchViewController];
[self addChildViewController:search];
[self.view addSubView:search.view];
}
also, make sure that when the search.view is added, it is already initialised.
Why you do not use NSObject class ?, i do not know your requirement , but if you want to store latest updated value in whole project(in execution) then you should use the singleton, but if you do not want to store value (i mean one result for whole project) then you should use NSObject derived Class. advantage is singleton consumes memory so memory will be wasted. NSObject class will be reusable and only allocated when it is required and then ARC will take care of all things. If you want to know how to create NSObject and use of it then you can give me reply.
Here is some code to load a XIB as part of a custom object with the object gets initialized.
Why are you not creating custom search component for search?
you can use this component all over the app.
also this is not creating duplicat code.
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[[[NSBundle mainBundle] loadNibNamed:#"SearchView" owner:self options:nil] objectAtIndex:0];
[self addSubview:self.view];
self.frame = self.view.frame;
}
return self;
}
Please check below code. Hope this is work for you.
- (void)viewDidLoad
{
if ([self.view viewWithTag:123456]) {
[[self.view viewWithTag:123456] removeFromSuperview];
}
MySearchViewController* search = [AppDelegate searchViewController];
search.view.tag = 123456; // give a any random tag to view
[self.view addSubView:search.view];
[self addChildViewController:search];
}
Please make sure given tag is not assign to other object except search.view in self.view.
Thanks
I have 4 ivars:
UIView *view1;
UIView *view2;
UIView *view3;
UIView *view4;
I would like to be able to alloc and init them in a dynamic way, instead of doing:
view1 = [[MyView1 alloc] initWithFrame:....
view3 = [[MyView2 alloc] initWithFrame:....
view4 = [[MyView3 alloc] initWithFrame:....
view4 = [[MyView4 alloc] initWithFrame:....
So, I tried to use a array and store the names of these ivars in it:
[array addObject:#"view1"];
[array addObject:#"view2"];
[array addObject:#"view3"];
[array addObject:#"view4"];
And so that within a loop I would do:
[self valueForKey:[array objectAtIndex:x]] = [[[[self valueForKey:[array objectAtIndex:x]] class] alloc] initWithFrame:(CGRect){.....
The above generates the error:
Expression is not assignable.
Hope someone can tell me why the above cannot be done.
My question is:
I have a feeling that this is not a clever way of doing things.
Right now I have only 4 views, but who knows if in the future I might have more.
So, my idea is that instead of hard-coding things, I would like to find a more dynamic way of accomplishing this.
My idea is that at compile-time, all this views are just of UIViews.
And only until run-time would I resolve these views to the individual class types
(i.e. MyView1, MyView2 etc etc) and alloc and init them and assign them
accordingly to the ivars (i.e. view1, view2, view3, etc) within my class.
The reason why I use an array is because, if in the future I added another view called view5 of class type MyView5, I could loop the alloc and init process using [array count]. If this way of doing it is still not optimal, please correct me.
To sum up, I would like to set up my controller in a way that it only knows during compile-time that these objects are just of class type UIView. Only until run-time would I resolve them individually to MyView1, MyView2(subclass of UIView) etc and assign them to the ivars within my controller (again, they are named view1, view2 etc).
And if I added another view in the future, I wouldn't have to look all over the place within this controller and hard-code: view5 = [[MyView5 alloc] init....
Can someone show me how to accomplish this optimally (future-wise) and dynamically?
EDIT:
It just occurred to me: it would be even better if I could create these ivars only during runtime, so that in the future everything could be created dynamically.
If I understand what you're asking, let me provide a different approach which you might like:
// Set up a mutable array of objects
NSMutableArray *views = [[NSMutableArray alloc] init];
// Set up an array of strings representing the classes - you can add more
// later, or use -stringWithFormat: to make the class names variable
NSArray *classes = #[#"MyView1", #"MyView2", #"MyView3", #"MyView4"];
// Now loop through it and instantiate one of each kind
for (NSString *className in classes)
[views addObject:[[NSClassFromString(className) alloc] initWithFrame:CGRectZero]];
Remember to be careful with NSClassFromString, as you might accidentally send the -initWithFrame: message to a type that doesn't implement that.
Hope this helps!
Edit: I see that I have given you an overly implementation-based answer, while you seem to be looking for the program design aspect.
When you're designing your controller class, it's good that you're considering how you will use the class in the future. That said, you need to have a specific idea of how abstract you want the class to be. In other words, don't go trying to make the controller class completely decoupled, because at some point your class will be a bulk of useless management code.
So how do you go about writing a class that is both decoupled and functional at the same time? I suggest you look for examples in Apple's classes. Here are a few:
UIViewController, probably the most important and versatile class on iOS. They designed it to be easily subclassable, yet there are also many premade subclasses like the navigation controller and table view controller varieties.
UIDocument, a template for all document model objects you will ever need. The system implementation handles all the nitty-gritty of iCloud sync, file management, etc., while not knowing anything about the document contents itself. The user subclass, however, provides the necessary information in an NSData object.
UIGestureRecognizer, the foundation of the touch-based UI. Most people only use the system-provided tap/swipe/pinch subclasses, but the abstract superclass itself (whether subclassed or not) detects any gesture you want and sends the necessary messages. Again, the gesture recognizer doesn't know what views you attach it to, yet it still performs its job.
Do you see what I'm getting at here? Apple's classes illustrate that there are ways to provide the necessary functionality while staying abstract, but without going into runtime acrobatics. As one of the commenters suggested, all you really need is an array of view objects. Instead of having your controller class instantiate the views, maybe you should have your client objects do that. It's all about finding a balance between abstraction and functionality.
The problem is, that you're thinking about this, as if your calls to the array elements would replace your code before compiling (like macros would do). It just doesn't work that way. For example:
[self valueForKey:[array objectAtIndex:x]] = ...
The compiler sees [self valueForKey:[array objectAtIndex:x]] as a value and "thinks" hey you can't assing sth to a value.
Try the following (i've splitted it into multiple statements for better readability and also to make the code more self-explanatory):
string className = [array objectAtIndex:x];
Class classToInit = NSClassFromString(className);
UIView *viewToInit = [[classToInit alloc] initWithFrame:...];
[self setValue:viewToInit forKey:className];
Keep in mind that with this approach the property names (and btw. they also need to be properties not ivars for KVC to work) must match your class names, i.e. if your class is named MyView1 your property must also be called MyView1. You might not want this (in fact according to you description in the text, you don't). So to make it work you could create a dictionary mapping your property names to your class names and enumerate over it's keys:
NSMutableDictionary classNameMapping = [NSMutableDictionary new];
[classNameMapping setObject:#"MyView1" forKey:#"view1"];
[classNameMapping setObject:#"MyView2" forKey:#"view2"];
//...
foreach (string propertyName in [classNameMapping allKeys])
{
string className = [classNameMapping objectForKey:propertyName];
Class classToInit = NSClassFromString(className);
UIView *viewToInit = [[classToInit alloc] initWithFrame:...];
[self setValue:viewToInit forKey:propertyName];
}
Recently I wrote some code where I tried to refer to an outlet on a UIViewController I'd just instantiated with [storyboard instantiateViewControllerWithIdentifier] and modify the subview that the outlet pointed to before presenting the ViewController. It didn't work because the ViewController's view hadn't loaded its subviews yet, including the one that my outlet referred to, so the property just gave me a null pointer.
After (with some struggle) tracking down the cause of my issue in the debugger, I Googled around and learned, through answers like this one, that I can cause the view to load its subviews without being displayed by calling the myViewController.view getter. After that, I can access my outlet without any problems.
It's a clear hack, though, and Xcode - quite rightly - doesn't like it, and angrily protests with this warning:
Property access result unused - getters should not be used for side effects
Is there a non-hacky alternative way to do this that doesn't involved abusing the .view getter? Alternatively, are there canonical/idiomatic patterns for this scenario involving something like dynamically adding a handler to be called as soon as the subviews are loaded?
Or is the standard solution just to replace myViewController.view with [myViewController view] to shut up Xcode's warning, and then live with the hack?
On iOS 9 or newer, one can use:
viewController.loadViewIfNeeded()
Docs: https://developer.apple.com/reference/uikit/uiviewcontroller/1621446-loadviewifneeded
I agree that forcing a view to load should be avoided but I ran into a case where it seemed the only reasonable solution to a problem (popping a UINavigationController containing a UISearchController that had yet to be invoked causes a nasty console says warning).
What I did was use new iOS9 API loadViewIfNeeded and for pre-iOS9 used viewController.view.alpha = 1.0. Of course a good comment above this code will prevent you (or someone else) removing this code later thinking it is unneeded.
The fact that Apple is now providing this API signals it can be needed from time to time.
Not sure how much cleaner this way, but it still works fine:
_ = vc.view
UPD: for your convenience, you can declare extension like below:
extension UIViewController {
func preloadView() {
let _ = view
}
}
You can read explaination by following URL: https://www.natashatherobot.com/ios-testing-view-controllers-swift/
merged Rudolph/Swany answers for pre ios9 deployment targets
if #available(iOS 9.0, *) {
loadViewIfNeeded()
}
else {
// _ = self.view works but some Swift compiler genius could optimize what seems like a noop out
// hence this perversion from this recipe http://stackoverflow.com/questions/17279604/clean-way-to-force-view-to-load-subviews-early
view.alpha = 1
}
If I understand you correctly, I think there's another fairly standard solution: move the outlet modification/configuration code into a viewDidLoad method (of the recently instantiated VC).
The topic is also discussed in this question.
It would require some restructuring, but it might give you a "cleaner" design in terms of MVC if your incoming VC handled its own configuration, and it would avoid the "You should never call this method directly" stricture on loadView.
You can call [myViewController loadView] to explicitly load the view, instead of abusing the .view getter. The .view getter actually calls loadView if necessary when called.
It's still not a very nice solution, since the UIView Documentation's section on loadView explicitly instructs that
You should never call this method directly
I have a small question when programming objects in objective-C. I have an App that is just about complete and everything works fine. My question is that I set my objects to nil and release them at appropriate times.
But is this enough or when and where should I use removefromsuperview?
In the case of adding a UIButton to a UITableViewCell I add the UIButton with the following code:
UIButton *buttonReset = [UIButton buttonWithType:UIButtonTypeContactAdd];
buttonReset.frame = CGRectMake(250.0f, 7.0f, 75.0f, 30.0f);
[cell addSubview:buttonReset];
buttonReset addTarget:self action:#selector(resetSettings) forControlEvents:UIControlEventTouchUpInside];
buttonReset = nil;
[buttonReset release];
Do I also need to use
[buttonReset removeFromSuperview];
in this case?
buttonReset = nil;
[buttonReset release];
This doesn't make sense. You set a pointer to nil (null pointer) and then send a message to it. In most other languages this would result in a crash. In Objective-C it's allowed, but nothing will happen. You have to release before setting to nil. But you shouldn't do neither in this case, because buttonReset is an autoreleased object (you didn't use alloc/init to create it), so you don't own it and therefore you must not release it.
You also don't have to use removeFromSuperview in this case. You add a button (a subview) to your cell (the superview). The superview will hold a strong (retaining) reference of the button. When the cell is then released, it will also handle all of its subviews. You only have to remove it yourself when you actually want to do that, but not for memory management reasons.
If you didn't already know about it, you might want to consider using Automatic Reference Counting (ARC) in the future.
No, you should not call [buttonReset removeFromSuperview];, at least not right away: if you do, the button would disappear from screen (given the name of the method, this should come as no surprise). Moreover, you do not need to set your button to nil.
Calling removeFromSuperview is needed when you need the control to be dropped from the screen. If you also release it, the object representing your control would be destroyed. For example, if you added a button programmatically for a specific task, and have to remove that button once the task has been accomplished, calling removeFromSuperview is appropriate.
Calling removeFromSuperview on a view causes it to be removed from its superview. This will make the targetted view disappear from the screen with all the view it contains.
In your situation, I would just set the object to nil and be done with it.
See does removefromsuperview releases the objects of scrollview?.
There are interesting informations in it.
but it's worth digging deeper into this, because it's a very important
concept in ObjC. You should never call -release on an object you
didn't -retain explicitly or implicitly (by calling one of the Three
Magic Words). You don't call -release in order to deallocate an
object. You call it to release the hold you have put on the object.
Whether scrollview is retaining its subviews is not your business (it
does retain its subviews, but its still not your business). Whether
-removeFromSuperview calls -release is also not your business. That's betweeen the scrollview and its subviews. All that matters is that you
retain objects when you care about them and release them when you stop
caring about them, and let the rest of the system take care of
retaining and releasing what it cares about.
you should use just the
[buttonReset removeFromSuperview];
and then
buttonReset = nil;
as apple saying
If the receiver’s superview is not nil, the superview releases the receiver. If you plan to reuse a view, be sure to retain it before calling this method and release it again later as appropriate.
in UIView Referance
So, I've been looking around at how to pass a block to a button as a selector, and the answers seem more complicated than I need.
If I subclass UILabel with a method called doBlock; and a block variable called blockToDo, and all the method does is call blockToDo (); couldn't I do something like:
myLabel.blockToDo = ^myBlock;
[myButton addTarget: myLabel action: #selector (doBlock) forControlEvents: UIControlEventTouchUpInside];
Or is that going to get me into some kind of trouble I'm not aware of?
Edit: So, there's two things here: what I really wanted to do, and the answer to the question I asked. They're different things.
Another way to phrase my question might be to split it in two. One half would be "if I want to add a simple method to UILabel, can I just subclass it and add that method, or are there a bunch of other things that a subclass of UILable is obligated to do?" The other half would be "adding a doBlock; method to a subclass seems to be the easiest way to pass a block as a selector, but all the suggestions on these boards seem to be to define a whole new class for that purpose, which seems like a lot of extra work. Why?"
It seems like most people tackled that second question, which is understandable, because it's actually the more obvious part of the original question as stated.
But, as to what I really wanted to do: what I've ended up doing is defining a category that has a doBlock; method, and using associative referencing to store a ^myBlock ivar within the category. This is very versatile, because I can just use the category to tack a block onto any class and it will then respond to #selector (doBlock).
So, I'm not sure how to wrap this up. The question I asked has been partially answered, but it turns out that what I really wanted to do isn't all that directly relevant to the question as formulated. How should I award a best answer?
I'd do something like the following. You could additionally make a category that made this easier. Note that addTarget:action:forControlEvents: does not retain the target.
Update: If you're using ARC, the best solution might be to make a category that assigns itself as an associated object. However, seeing as how long ago I originally wrote this answer, there are probably better solutions available.
BlockExecutor *executor = [BlockExecutor executorWithBlock:^(id sender) {
}];
[myButton addTarget:executor action:#selector(invokeWithSender:) forControlEvents: UIControlEventTouchUpInside];
BlockExecutor.h
typedef void (^BlockExecutorBlock)(id);
#interface BlockExecutor : NSObject
#property(nonatomic, copy)BlockExecutorBlock block;
+ (BlockExecutor*)executorWithBlock:(BlockExecutorBlock)block;
#end
BlockExecutor.m
#implementation BlockExecutor
#synthesize block = _block;;
+ (BlockExecutor*)executorWithBlock:(BlockExecutorBlock)aBlock
{
BlockExecutor *executor = [[BlockExecutor alloc] init];
executor.block = aBlock;
return [executor autorelease];
}
- (void)dealloc
{
self.block = nil;
[super dealloc];
}
- (void)invokeWithSender:(id)sender
{
if(_block)
{
_block(sender);
}
}
#end
What you describe won't "cause trouble" like a crash or a memory leak.
However, it is probably a violation of the MVC design pattern. Normally in MVC, a button (which is a view) sends a message to a controller. Then the controller either updates the label (another view) directly (if the button action shouldn't change the model), or the controller modifies the model, which causes the label to be updated appropriately.
You could easily end up with a retain cycle with this approach. Say the block references the controller:
label.blockToDo = ^{
[controller didTapLabel];
}
The block will retain the controller, so the controller can't be deallocated. Since the controller will never receive its final release, it won't remove its views, and the label won't release the callback block.
You can get around this by being careful about what's in the block, but I think it's cleaner to keep the block in your controller and invoke it via the selector attached to the label's target/action.