UITapGesture target not retained (with ARC) - ios

I create an UITapGesture with a target which is NOT the current object. Later, when tapping, app crashes.
View controller .h:
#interface ViewController : UIViewController
{
IBOutlet UIImageView *iv;
}
#end
View controller .c:
#import "ViewController.h"
#import "Target.h"
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
Target *t = [[Target alloc] init];
UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc] initWithTarget:t action:#selector(gestureDone:)];
[iv addGestureRecognizer:tgr];
[iv setUserInteractionEnabled:YES];
}
#end
Target.h:
#interface Target : NSObject
- (void)gestureDone:(UIGestureRecognizer *)gr;
#end
Target.c:
#implementation Target
- (void)gestureDone:(UIGestureRecognizer *)gr
{
NSLog(#"Gesture!");
}
#end
(My XIB file just contains one image...)
When tapping the image, it crashes. If for example I add an instance variable Target *t to my view controller (and remove local declaration in viewDidLoad), then no issue arises. When not doing that, I overrided dealloc in Target, put a NSLog there and saw that as soon as viewDidLoad finishes execution, the Target object is fred.
Am I doing anything wrong, or is it some issue? (Usually I'm not facing this problem because I use initWithTarget:self ...).

UIGestureRecognizer doesn't retain its target. Most objects that take a target/action pair do not retain their targets. This is mentioned in the Cocoa Fundamentals Guide/Communicating with Objects/The Target-Action Mechanism/The Target:
Control objects do not (and should not) retain their targets. However, clients of controls sending action messages (applications, usually) are responsible for ensuring that their targets are available to receive action messages. To do this, they may have to retain their targets in memory-managed environments. This precaution applies equally to delegates and data sources.
You need to make sure the target is retained some other way, such as by storing a reference to t in an instance variable of your ViewController.

Add an instance variable of the Target t to your ViewController class and make it strong. That way it stays in memory.

Related

if Delegate sends gesture messages do they conform to my customised Objective C protocol?

I am trying to send gesture messages from several subclassed UIViews to common handlers in a UIViewController called TouchController. I am using a customised protocol to address a question arising from the answer to my previous SO post.
Messages are now sent from OneOfSeveralSubviews using a customised protocol called FirstGestureRecognizerDelegate but none of the handlers respond even though the code compiles with 0 warnings. I am confident the problem is not in TouchController for reasons stated previously. And after looking at more references (below) and checking out possible duplicates (below) I am now looking for answers to a new set of questions.
See Note (below) for my response to a possible duplicate question.
Here is my customised protocol
OneOfSeveralSubviews.h
#import <UIKit/UIKit.h>
#protocol FirstGestureRecognizerDelegate <NSObject>
- (void)handleLongPress:(UILongPressGestureRecognizer*)gestureRecognizer;
- (void)handleTapSelect:(UITapGestureRecognizer*)gestureRecognizer;
- (void)handlePan:(UIPanGestureRecognizer*)gestureRecognizer;
#end
#interface OneOfSeveralSubviews : UIView
{
CGRect touchFrame;
}
- (id)initView:(CGRect)rect;
#property (nonatomic, weak) id<FirstGestureRecognizerDelegate> delegate;
#end
In the implementation of OneOfSeveralSubviews, gestures are defined by declaring instances of UILongPressGestureRecognizer, UITapGestureRecognizer and UIPanGestureRecognizer and setting their delegate properties
e.g.
UILongPressGestureRecognizer *longPressGR = [[UILongPressGestureRecognizer alloc] initWithTarget:(id)self.delegate
action:#selector(handleLongPress:)];
[longPressGR setDelegate:(id)self.delegate];
The program runs but is unresponsive. I’m not sure if this happens because it [a] sends messages that conform to a protocol the handlers ignore OR [b] can’t find a way to send messages that conform to a protocol the handlers recognise. References I’ve looked at give no clue whether it is [a] or [b]. Moreover this document uses the term subclassing to mean something different to subclassing in my situation and the illustrated code examples are in Swift rather than Objective C.
This leads me to ask:
can UILongPressGestureRecognizer, UITapGestureRecognizer and
UIPanGestureRecognizer actually be used with my customised
protocol ?
or does a customised protocol also require customised gesture
recognition methods rather than standard methods (i.e. pre-fixed
with UI) ?
either way, are there simple code changes that would let the
delegate send gesture messages that conform to the customised
protocol ? or
is it possible I’m trying to do something that is conceptually flawed ?
I’d welcome answers to these questions or a better question if there is one.
OneOfSeveralSubviews.m
#import "OneOfSeveralSubviews.h"
#implementation OneOfSeveralSubviews
- (id)initView:(CGRect)rect
{
self = [super initWithFrame:rect];
if (self)
{
// Set view size and color
UILongPressGestureRecognizer *longPressGR = [[UILongPressGestureRecognizer alloc] initWithTarget:(id)self.delegate action:#selector(handleLongPress:)];
[longPressGR setDelegate:(id)self.delegate];
[longPressGR setMinimumPressDuration:0.6f];
[longPressGR setNumberOfTapsRequired:1];
[longPressGR setNumberOfTouchesRequired:1];
[hotspot addGestureRecognizer:longPressGR];
// Create and configure two other gesture recognizers
self.userInteractionEnabled = YES;
}
return self;
}
#end
TouchController.h
#import <UIKit/UIKit.h>
#import "OneOfSeveralSubviews.h"
#import "TwoOfSeveralSubviews.h"
// #import "ThreeOfSeveralSubviews.h"
// #import "FourOfSeveralSubviews.h"
// etc.
#interface TouchController : UIViewController <FirstGestureRecognizerDelegate, SecondGestureRecognizerDelegate>
{
CGRect touchFrame;
CGRect screenFrame;
}
#property (strong, nonatomic) NSTimer *timer;
#end
TouchController.m
#import "TouchController.h"
#interface ViewController ()
#end
#implementation TouchController
- (void)viewDidLoad {
[super viewDidLoad];
OneOfSeveralSubviews *oneForAll=[[OneOfSeveralSubviews alloc] initView:[UIScreen mainScreen].bounds];
[self.view addSubview:oneForAll];
}
I've looked through these related posts:
Implementing a Custom Gesture Recognizer
UIGestureRecognizers
Working with protocols
Conventions
Avoid category method name clashes
Customizing Existing Classes
Handling app delegates and switching between views
differences between weak and assign property?
NOTE
I checked Scott’s duplicate question and it is definitely asking a question similar to those being asked here. But in Scott’s example it also looks as if delegation that originates inside the ViewController is destined for a target outside the Viewcontroller (i.e. in MakePlantTVC). Whereas in my case, delegation that originates outside the ViewController is destined for a target inside the ViewController. For that reason I would never have picked Scott’s question as a duplicate even though to a more experienced person such a difference might seem insignificant.
These possible duplicates are very similar but don't solve my problem:
Declare that delegate conforms to another protocol
How to conditionally conform to delegate protocol?
UIView setDelegate:]: unrecognized selector sent to instance

iOS compiling order

It is one of my first days at iOS programming. I came from C++ and have specific silly question. How does compiler work and go through the code?
How I understand all starts with public interface, then continues to private. How implementation works? Methods? Is view controller as main function in C++ and it goes through all methods at the start?
Here is my viewController .h and .m. This program already has some other classes and action buttons. Maybe someone can explain step-by-step. Thanks.
ViewController.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#interface ViewController : UIViewController
#end
ViewController.m
#import "ViewController.h"
#import "Deck.h"
#import "PlayingCardDeck.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UILabel *flipsLabel;
#property (nonatomic) int flipCount;
#property (strong, nonatomic) Deck *deck;
#end
#implementation ViewController
- (Deck *)lol
{
if (!_deck) _deck = [self createDeck];
return _deck;
}
- (Deck *)createDeck
{
return [[PlayingCardDeck alloc] init];
}
- (void)setFlipCount:(int)flipCount{
_flipCount=flipCount;
_flipsLabel.text = [NSString stringWithFormat:#"Count: %d", _flipCount];
NSLog(#"%d", self.flipCount);
}
- (IBAction)touchCardButton:(UIButton *)sender {
if ([sender.currentTitle length]) {
[sender setBackgroundImage:[UIImage imageNamed:#"cardback"] forState:UIControlStateNormal];
[sender setTitle:#"" forState:UIControlStateNormal];
}
else {
Card *randomCard = [self.lol drawRandomCard];
if (randomCard){
[sender setBackgroundImage:[UIImage imageNamed:#"cardfront"] forState:(UIControlStateNormal)];
[sender setTitle:randomCard.contents forState:UIControlStateNormal];
self.flipCount++;
}
}
}
#end
The beginning of the program code is similar to c++, it is the very c main() function.
Within your main the framework is called (by instaticating an UIApplication or subclass object) and the names of the application class and the application delegate class are handed over to the framework. The cocoa touch framework will then instantiate the application first and instantiate the delegate and assign the delegate to the application.
Typcially the first method from your program code that is invoked is the UIApplicationDelegate method application:willFinishLaunchingWithOptions: but the one that you typcially start with is application:didFinishLaunchingWithOptions:
There some preparation is set up, which is often not required especially when you work with a storyboard or when you set the main interface in your project properties. According to these properties or storyboard one view controller is the root view controller that is invoked first. For the view controllers it is quite similar. You would use NIB files or storyboards in most cases to define their UI Items and then viewDidLoad is the first method that is being invoked with regard to the root view.
Given your code you are likely to add something like this:
-(void) viewDidLoad {
[super viewDidLoad];
[self createDeck]
// here you may want to access some UIView objects in order to display your deck.
}
I don't know if you understand how c++ compiles, Objective-C is all the same.
First preprocessor translate all preprocess code(#import, #macro, etc), and then every .m file is considered a compile unit, they are compile separately in to object files without a specific order.
Then linker links all object files together.
If you don't understand what compile unit or object file are, I suggest you do more research on how preprocessor, compiler, linker works, since it is a too broad topic to explain here.

Accessing interface builder object from view controller

I'm completely new to Objective-C, XCode, and iOS development and I'm trying to figure out how to run certain code at startup, after all UI views and controls have been instantiated. I have a generic NSObject that I've added through interface builder by dragging it into my view controller scene. It's defined as follows:
#import <Foundation/Foundation.h>
#interface Controller : NSObject {
IBOutlet UISlider *slider;
IBOutlet UILabel *label;
}
-(IBAction)sliderChanged:(id)sender;
#end
I need to run sliderChanged on initialization. I've tried the following way:
#import "Controller.h"
#implementation Controller
-(id)init {
self = [super init];
if (self){
[self sliderChanged:nil];
}
return self;
}
// More code here
But both my slider and label are nil when this is called. I understand there's a viewDidLoad method within the ViewController class which may be what I need, but I'm not sure how to access the instance of my Controller class (which seems to be instantiated somewhere behind the scenes by the interface builder) from within that method. Should all of this code simply be moved to the ViewController itself? That would seem to make sense, but the design above is what we've been instructed in class, so I'm not really sure how to go about doing this.
After the XIB/Storyboard loader finishes loading all the objects and wiring them up, it sends awakeFromNib to every object that was instantiated from the XIB. So try adding this to your Controller class:
- (void)awakeFromNib {
[super awakeFromNib];
[self sliderChanged:nil];
}
You can find more information in the NSObject UIKit Additions Reference and “The Nib Object Life Cycle” in the Resource Programming Guide.
HOWEVER, if you created Controller as a top-level object, and you didn't connect any outlets to it, then nothing references it after the XIB loader finishes with it, so the system will deallocate it again. That's probably not what you want, so you should connect an outlet in your view controller to the Controller. If you do that (and let's say the outlet is named controller), then you can access it in viewDidLoad in your view controller class:
#import <UIKit/UIKit.h>
#import "Controller.h"
#interface ViewController : UIViewController {
IBOutlet Controller *controller;
}
#end
Implementation:
- (void)viewDidLoad {
[super viewDidLoad];
[self.controller sliderChanged:self];
}

Need to call methods in other viewControllers from another viewController

I have an app that has multiple viewControllers, where some of these viewControllers contain methods that run various tasks. What I need to do is when the initial viewController loads, is to call these methods in the other viewControllers such that they run in the background, however, I am having some difficulty doing this.
Let's say I have 4 viewControllers, A, B, C, & D, where A is the initial viewController, and in each viewController, I have aMethod, bMethod, cMethod, and dMethod respectively. Here is the relevant code:
Inside my opening viewController (AviewController):
in the .h file:
#import "BViewController"
#import "CViewController"
#import "DViewController"
#interface AViewController:UIViewController {
BViewController *bViewCon;
CViewController *cViewCon;
DViewController *dViewCon;
}
#property (nonatomic, retain) BViewController *bViewCon;
#property (nonatomic, retain) CViewController *cViewCon;
#property (nonatomic, retain) DViewController *dViewCon;
#end
In my .m file I have the following:
#import "BViewController"
#import "CViewController"
#import "DViewController"
#implementation AviewController
#synthesize bViewCon, cViewCon, dViewCon;
- (void) viewDidLoad {
[super viewDidLoad];
bViewCon = [[BViewController alloc] init];
[bViewCon bMethod];
...
}
However, I am getting the error message, "No visible #interface for 'BViewController' declares the selector 'bMethod'". I need to call the other methods from the other viewControllers the same way from this class (i.e. AViewController).
Thanks in advance to all who reply.
Have you considered using NSNotificationCenter? Set up the methods on the notifications and just ping them when you need them to run. This helps if your other view controller is instantiated and available, like buried in the navigation controller stack or on a separate tab.
To answer you question about that error, you need to declare the method you want to call in your header file. The error is stating it can't find a declaration for that method.
Example of notification center
// listen for notifications - add to view controller doing the actions
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(mySpecialMethod) name:#"SomeNotificationName" object:nil];
// when you want your other view controller to do something, post a notification
[[NSNotificationCenter defaultCenter] postNotificationName:#"SomeNotificationName" object:nil];
// you don't want this notification hanging around, so add this when you are done or in dealloc/viewDidUnload
[[NSNotificationCenter defaultCenter] removeObserver:self]; // this removes all notifications for this view
// if you want to remove just the one you created, you can remove it by name as well
To solve the error you're receiving, make sure that all of the methods are declared in the header (.h) files of each controller (otherwise, the compiler won't be able to see them).
As all of these controllers are children to AViewController (they are created by AViewController and kept as ivars on it), I wouldn't use NSNotificationCenter here (unless there are other objects that need to be notified in case of certain events occurring too, which aren't owned by AViewController).
Instead, I would just call the methods directly as you're attempting to do.
On another note, if these methods are starting ongoing tasks (running tasks in background), it may be best for you to move the method calls to the init: method of AViewController. (As on iOS 5, views CAN be unloaded and hence viewDidLoad: can be called potentially more than once... such as in the case of memory warnings and the view being off screened). I might go about doing something like this:
- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)bundle
{
self = [super initWithNibName:nibName bundle:bundle]; // your correct stuff here
if (self)
{
bViewCon = [[BViewController alloc] init];
[bViewCon bMethod];
// ... and so on for the other controllers
}
return self;
}
Edit
Although, as mentioned in a comment, UIViewControllers aren't exactly cheap in terms of memory... it honestly would likely be best to refactor this code to have a single controller (a subclass of NSObject instead of UIViewController, which is cheaper) to act as a manager for the tasks that are going to be running in the background. I imagine this will likely help you later down the road too, as it would help compartmentalize the tasks and purpose of each of your controllers (in such, UIViewControllers should primarily be responsible for managing a view (/view hierarchy in some cases) and associated tasks... if there are ongoing tasks that are occurring outside the scope of things associated with said view, it's probably a sign that the UIViewController shouldn't be handling them...

Memory management, things to be clear

I need to get things clear about Objective-C memory management:
If I declare an object in the class header as ivar without #property:
#interface MyFacebooDelegate : UIViewController
{
TableViewController *tableController;
}
...
#end
and some where in the code for example in - (void)viewDidLoad I do :
tableController = [[TableViewController alloc] init];
so where is best way to release it. What if I make the instant object a property what will be the different? and how the memory management will be too
#interface MyFacebooDelegate : UIViewController
{
TableViewController *tableController;
}
...
#end
#property (nonatomic, strong) TableViewController *tableController;
What the following syntax do exactly for the object viewController:
.h
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) ViewController *viewController;
#end
.m
#implementation AppDelegate
#synthesize window = _window;
#synthesize viewController = _viewController;
- (void)dealloc
{
[_window release];
[_viewController release];
[super dealloc];
}
.....
#end
If I want to return an object through a method to another class, do I need to autorelease it in the method body first and then retain it in receiver side?
for example this method what exactly to do in the method body and in the receiver side too:
-(NSString *)getFriendId
{
NSArray *ar = [NSArray arrayWithObjects:#"1",#"2",#"3", nil];
return [ar objectAtIndex:0];
}
I know this a lot but I am really confused and need your help.
1) best way is in dealloc; or right before re-setting it.
2) a property does the retain/release for you. But WARNING! You keep mixing up things. You use "strong" here, which relates to ARC. If you really insist on using classic retain/release (you shouldn't) then use (nonatomic, retain) instead.
3) Your properties get deallocated on dealloc. Again, strong is wrong here.
4) Yes. Ideally you should. Another reason why ARC is awesome, it does this all for you, automatically.
tl;dr: Use ARC. Never go back. (But still learn manual memory management)
ARC is the answer for your all memory management question. Very import note on Strong and Weak property in addition to ,
iOS Strong property: So strong is the same as retain in a property declaration before ARC. For ARC projects I would use strong instead of retain, I would use assign for C primitive properties.
iOS outlets should be defined as declared properties. Outlets should generally be weak, except for those from File’s Owner to top-level objects in a nib file (or, in iOS, a storyboard scene) which should be strong. Outlets that you create will therefore typically be weak by default, because: Outlets that you create to, for example, subviews of a view controller’s view or a window controller’s window, are arbitrary references between objects that do not imply ownership.

Resources