iPhone sdk Storing the block parameter for later use - iphone

I have 2 classes:
Class A:
Registered as observer for NSNotification with name notificationName
.h file declares - typedef void (^block1)(NSError *error);
Private instance variable - block1 _block1_obj;
Then have 2 methods like these:
- (void) doSomethingWithCompletionHandler:(block1)handlerBlock
{
_block1_obj = handlerBlock; // Also tried _block1_obj = [handlerBlock copy];
[classB_obj doAnotherThing];
}
- (void) notificationHandler:(NSNotification *)notification
{
...
_block1_obj(error);
}
Class B:
- (void) doAnotherThing
{
...
[[NSNotificationCenter defaultCenter] postNotificationName:notificationName
object:nil
userInfo:dict];
}
Now I found while debugging that,
_block1_obj is assigned a value in doSomethingWithCompletionHandler
But after some time when control comes in notificationHandler:
_block1_obj is nil.
Not sure why is this. Can any one please explain?
Update:
I also tried creating a property with copy attribute but it did not work too.

You need to copy the block from stack to heap. If is insufficint to declare a copy block if you don't use it, but assign the ivar direcrly without copy.

You need to declare the instance variable as __strong, so ARC will know it has to retain the object.
I would suggest you use a private property, something like this in your .m file:
#interface YourClass ()
#property (strong, nonatomic) block1 block1_obj;
#end
#implementation YourClass
#end
and then assign the block by using the accessor:
self.block1_obj = handlerBlock;

Related

using a delegate in a class method

I want to invoke a delegate in class method.
The example below obviously does not work, since the delegate is an instance variable that is accessed within a class method. (Error: instance variable 'delegate' accessed in class method)
Is there an alertnative?
My header file:
// MyClass.h
#import <Foundation/Foundation.h>
#protocol MyDelegate <NSObject>
-(void)update;
#end
#interface MyClass : NSObject
{
id<MyDelegate> delegate;
}
#property (nonatomic, retain) id delegate;
+(void)methodThatInvokesDelegate;
#end
My implementation file:
// MyClass.m
#import "MyClass.h"
#implementation MyClass
#synthesize delegate;
+(void)methodThatInvokesDelegate{
[delegate update];
}
#end
Three obvious options:
Singleton
Static variable (i.e., class variable) pointing to the delegate
Use NSNotification's rather than delegates
Since a singleton (and a static variable) can't keep track of the lifecycle of delegates, I think option three would be the cleanest.
I want to know the context, which let you run in that situation. ;-) Anyway:
First: Delegates are set for a specific instance object. Because of this, you can have different delegates for different instances of the same (delegating) class.
Second: A class method runs inside a class object of that class. This is an object that is different from every instance object of that class. So there is nothing that can be called "the delegate". You can have 100s of delegates.
Third: Your class object needs a delegate at its own. So you have to add a property delegate to the class object and then use this. (Yes, it is possible to have properties an a class object. I did not write declared property.) If you need further information on how to do this, just comment it. I will add code.
I'm not sure if this will help you, but I have a similar situation where I have a class method used for data loads. In this case, the class instantiates itself (so that the caller doesn't need to) until it is done. (this code was edited somewhat to make it work here)
header file:
#protocol DataLoaderDelegate2 <NSObject>
- (void) dataLoaderSuccess:(NSData *)data loader:(id)theloader;
- (void) dataLoaderFailed:(NSString *)error loader:(id)theloader;
#end
#interface DataLoader2 : NSObject {
NSURLConnection *conn;
NSMutableData *receivedData;
NSFileHandle *fileHandle;
id <DataLoaderDelegate2> delegate;
}
#property (nonatomic, assign) id<DataLoaderDelegate2>delegate;
Call to start the process - the call to initWithRequest passes "self" along.
+ (DataLoader2 *)loadWithURLRequest:(NSURLRequest *)req delegate:(id)_delegate
{
DataLoader2 *dl = [[DataLoader2 alloc] init];
[dl setDelegate:_delegate];
conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
return dl;
}
When the data is done loading, it cleans up with something like
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if ([delegate respondsToSelector:#selector(dataLoaderSuccess:loader:)])
[delegate dataLoaderSuccess:(fileHandle)?(id)fileHandle:(id)receivedData loader:self];
[self autorelease];
}

Using class extensions in xcode 4.4

Since xcode 4.4 you don't need to #synthesize properties anymore (see here), the compiler does it for you. So, why does the compiler complain
use of the undeclared identifier _aVar
in my viewDidLoad method of ViewControllerSubclass:
#interface ViewController : UIViewController
#property (assign, nonatomic) int aVar;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.aVar = 5;
NSLog(#"Super value: %d", _aVar);
}
#end
#interface ViewControllerSubclass : ViewController
#end
#interface ViewControllerSubclass ()
#property (assign, nonatomic) int aVar;
#end
#implementation ViewControllerSubclass
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"Subclass value: %d", _aVar);
}
#end
If I move everything to the one file instead of 4 separate files for the respective interfaces and implementations, the compiler instead complains that _aVar is private. But _aVar should have been automatically synthesized in my ViewControllerSubclass.
Still keeping everything in 1 file, if I move the initial property declaration to a class extension:
#interface ViewController ()
#property (assign, nonatomic) int aVar;
#end
The build still fails saying that _aVar is private.
If I go back to the 4 file setup for the respective interfaces and implementations xcode builds without even a warning.
If I then run the code:
[[[ViewControllerSubclass alloc] init] view];
the log statements in the above examples print out the following:
Super value: 0
Subclass value: 5
It makes sense that NSLog(#"Super value: %d", _aVar); produced a result of 0 because this variable is supposed to be private to the superclass. But then, why does NSLog(#"Subclass value: %d", _aVar); produce a result of 5??
This is all very odd.
You are confusing several different issues, and I'm somewhat confused when you talk about jumping between files and you don't specify where your errors are happening.
Anyway, there is the issue of instance variable visibility. If you declare your iVars within the interface scope, they are, by default, protected.
#interface Foo : NSObject {
int protectedInt;
#private
int privateInt;
#public
int publicInt;
}
#end
When you synthesize iVars, the instance variables themselves are private, unless you explicitly specify them.
Methods will always fire on the most derived implementation.
Now, when you call this...
[[[ViewControllerSubclass alloc] init] view];
You will allocate a subclass, initialize, and cause the view to be loaded. This code will execute...
#implementation ViewControllerSubclass
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"Subclass value: %d", _aVar);
}
#end
The first thing it does is call the base class implementation...
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.aVar = 5;
NSLog(#"Super value: %d", _aVar);
}
#end
Of course, it calls super, but that part's not important here. The next line assigns 5 to self.iVar. But, which iVar? It calls the property setter method on this object. What type is this instance? It's a ViewControllerSubclass. Since you have given both your base class and its subclass the same name (and declared the property as part of the class extension), they each have their own private-scope instance variable .
However, a method is called on the most derived implementation. Thus, self.iVar will set the instance variable of the subclass. The instance variable for the base class remains unchanged.
When you NSLog the value, you are accessing the private instance variable of the base class, which has not been changed.
Now, after the base class viewDidLoad finishes, we get the code running for the subclass. It logs the value of its private instance variable, which was changed as a result of the base class calling the property setter. So, it will now print it's value, which is 5.
When you make the superclass declaration public, the compiler won't attempt to re-synthesize the property; it assumes that's been taken care of in the superclass. Thus, _aVar is not in scope anywhere in the subclass. It's private anyway, so even when you put them all in the same file that's why you see those errors.
However when you make the superclass property declaration inside the class extension, the compiler will auto-synthesize the property for both the superclass and the subclass. This ends up with both classes having private instance variables _aVar (with two distinct addresses). However, when the superclass viewDidLoad method sets the property, the method invokes the subclass's accessors, which set the value of the subclass's private _aVar variable, and not the superclass's. So that explains why you see the superclass value not changing.
Hope this helps!
I just tested your setup and could replicate your error. I came to the following conclusion:
You need to declare your #property in a .h file. If you want a private variable, declare it in .m in the category #interface (the one with the parentheses).

protocol method is not being called by the delegate object

Another case of protocol method not being called - NO idea what am I doing wrong here...
Here is the code, omitting unnecessary info...
first header file: AccessNumber.h
#protocol AddItemDelegate <NSObject>
#required
- (void) processSuccessful: (BOOL)success;
#end
#interface AccessNumber : UIViewController <UITableViewDelegate, UITableViewDataSource, ABPeoplePickerNavigationControllerDelegate, UIAlertViewDelegate> {
id <AddItemDelegate> delegate;
}
#property (retain) id <AddItemDelegate> delegate;
#end
first .m file: AccessNumber.m - I am calling the protocol method from viewdidload just for testing purposes, it should basically get called from another method, but same thing for the sake of this convo (tried both of course)
#import "AccessNumber.h"
#import "History.h"
#synthesize delegate;
- (void)viewDidLoad
{
[super viewDidLoad];
....
[[self delegate] processSuccessful:YES];
}
second file header: History.h
#interface History : UIViewController <UITableViewDelegate, UITableViewDataSource, AddItemDelegate> {
....
}
method implementation in history.m
- (void) processSuccessful: (BOOL)success {
NSLog(#"SUCCESS");
}
Appreciate any help. Thanks!
In the code i don't see something like:
theAccessNumber.delegate = self;
You must set the History instance as the delegate property of the AccessNumber instance.
Regards.
The code seems a little bit funny but you are not telling your "AccessNumber" class who is his delegate, even though you are making the "History" class implement the protocol established by yourself (otherwise you would get a warning).
You have to set the delegate for the "AccessNumber" class like this when setting it up from within "AccessNumber.m":
self.delegate = historyInstance;
or like this when setting it up from within "History.m":
accessNumberInstance.delegate = self;
There are very vew situations where you should retain a delegate, normally your delagate will outlive the object. So change your property from retain to assign. And be sure you are setting the delegate. Where are you doing it? If your object really depends on it you should be passing it in the constructor (iniWithDelagate). Try doing a NSLog before calling the delagate method just to see if it isn't nil.

Proper way to make variable accessible within entire .m file?

As you can tell, I'm new to Objective-C. I currently have a Singleton working, but I'm trying to use it throughout several methods within the same .m file.
Right now I use this to declare/instantiate the Singleton within a method:
GlobalData *globDat=[GlobalData getInstance];
Do I need to declare it within each method, or is there a way to do this at the top of the .m (or .h?) file so I can access it throughout the other methods?
Thanks...
(BTW I've tried placing the line of code shown above under my "#implementation" line, but I get an error: "Initializer element is not a compile-time constant" which I now know is because the line is not within a method.)
You can add a class extension, and store the global data in ivar, like this:
#interface MyClass() {
GlobalData *globDat;
}
#end
#implementation MyClass
-(id) init {
self = [super init];
if (self) {
globDat=[GlobalData getInstance];
}
return self;
}
#end
This will hide the globDat from the interface, and make it available throughout the methods you implement inside your implementation block of MyClass.
when u need a variable / object that is accessible to all methods in an implementation data, u need to declare it on the header file, and then synthesize it on the implementation file (note that u may need to import the GlobalData header)
so
in header (.h):
#class GlobalData;
#properties (nonatomic, strong) GlobalData *globDat;
and then at the implementation file (.m)
#import "blablabla.h"
#import "GlobalData.h"
#implementation blablabla
#synthesize globDat;
-(void)viewDidLoad
{
globDat = [GlobalData getInstance];
}
good luck

Category-like extension for instance variables

Is there a way to somehow emulate category behavior for a class regarding to it's instance variables, not methods ?
I have a ClassA, and I want to keep its name after extending it with new methods AND ivars from other cllass (ClassB).
Of course, I can inherit ClassA, but resulting class will have different name.
For methods addition, it's not a problem - category would be a good solution.
UPDATE: ClassA used as file owner for a XIB, and these fields to be extended are IBOutlets. So I need them at build phase.
Since the iPhone uses the modern Objective-C runtime, you can use associative references to add data to instances without having to declare instance variables. See the documentation for objc_setAssociatedObject etc.
If you wrap the calls to the runtime in standard accessor methods, it will be very easy to use.
I've investigated this question playing around associative references (thanks to Ole), with methods static variables, methods swizzling, and finally come to this simple solution (no runtime stuff). I simply use "categorized" class only to return a pointer to a derived class, which of course can contain additional ivars. Doing so I achieve one unexpected benefit: I can call super's class methods, which is impossible when extending through categories.
Example of a class extension (tested):
ClassA+ClassB.h
#protocol _ClassB_Protocol
#optional // to avoid warnings
- (IBAction) onClick:(id)sender;
#property (nonatomic, retain) IBOutlet UIButton *aButton;
#end
#interface ClassA (_ClassA_Category) <_ClassB_Protocol>
#end
#interface ClassB: ClassA <_ClassB_Protocol> {
UIButton *aButton; // _ivar_ to add
}
#end
ClassA+ClassB.m
#implementation ClassA (_ClassA_Category)
// this will be called first on [ClassA alloc] or [ClassA allocWithZone:(NSZone *)zone]
+(id) alloc {
if ([self isEqual: [ClassA class]]) {
return [ClassB alloc];
} else {
return [super alloc];
}
}
#end
#implementation ClassB: ClassA
#synthesize aButton;
-(void) dealloc {
[aButton release];
[super dealloc]; // this is impossible for an ordinary category
}
- (void) onClick:(id)sender {
// some code here
}
#end
Now we have in the same time:
ClassB "extends" ClassA (category way);
ClassB inherits ClassA (ClassB can call ClassA methods);
ClassB can be accessed through ClassA name (category way)
I put Martin's example into a trivial app replacing ClassA with NSData, ClassB with XXData, and onClick with getIvar, and invoked it (Mac OS X 10.6.6, Xcode 4 Final) with:
NSData * data = [NSData data];
NSLog(#"%#", [data getIvar]);
It fails with "-[NSConcreteData getIvar]: unrecognized selector sent to instance" ..
It fails because "alloc" in the NSData category (which returns the pointer to the derived class) is not called by the above code. If, instead, "alloc" is called explicitly, as in:
NSData * data = [[NSData alloc] init];
NSLog(#"%#", [data getIvar]);
then all is well.