I've seen a lot of discussions NEAR this subject, but none that actually work in Xcode 5.x, especially using ARC. I have a simple problem:
I need to pass a method reference to a CreateButton method so that when the button is called it calls my custom function, and not some generic one.
I've tried using (SEL) type, but that doesn't work with ARC. I've tried using the &func method, but that claims I haven't declared the function yet.
So my need is:
Class A calls Class B and sends over the info to make a UIButton. Within that call, I want to send over the action:method in a reference. I'm sure this is done routinely, but I can't seem to find an iOS 7 / Xcode 5.x method of doing it. I've also reviewed the O'Reilly iOS 7 fundamentals and cookbook code and couldn't find this discussed anywhere.
Thanks for you help.
When I have to pass selectors around, I convert them to strings with NSStringFromSelector() and back to selectors with NSSelectorFromString().
Passing the strings around is a lot easier. You can store them in collections (arrays, dictionaries), serialize and unserialize them, and they will work naturally with ARC.
Example:
In your class A where you gather the information to create a button:
NSString *selectorString = NSStringFromSelector(#selector(yourActionMethodNameHere:));
// Gather more information needed by Class B here, then package
// it all up into a dictionary, for example
NSDictionary *buttonInfo = #{#"selectorString": selectorString, /* more stuff here */};
At this point, you can call your button-constructing method in Class B, passing along buttonInfo, which contains all the information that that helper method needs, including the selector. The method can convert the string back to a selector and use it like this:
SEL actionSelector = NSSelectorFromString(buttonInfo[#"selectorString"]);
// configure your button to use actionSelector here
You should be able to use SEL parameters? I know I have done before.
ARC might complain and give you a warning, but it won't fail to compile. It's simply a warning because it can't quite figure out what to do memory wise.
If you really can't get that to work though, another alternative would be to use a block, so you might call your method like
[objectA performMethodWithParam:paramA paramb:paramB completion:^{ ... do somethhing ... }];
Then in that method you can just call
completion();
Instead of actually calling a method.
Another alternative would be to use the delegate pattern. Create a #protocol defining a method such as classADidFinish then make class B implement that method. Then set the instance of classB as the delegate for your classA instance, and have it call that method when it's done.
Both of these approaches will stop ARC moaning at you.
But as I said, using SEL params should work fine. There is a way you can even get the compiler to stop showing you the warnings but it's a little ugly.
Related
I'm looking into a project, trying to get a grip on the code. I just ran in to a #selector. As far as I understand it, selectors call methods (I'm sure there's more to it, of course).
One thing that struck me is that the selector call doesn't pass an argument to the method even though the method itself takes a parameter. The code works fine, so I take it that this is normal behavior with a selector. Here is a sample of the code:
[Communication sendVerifyNumber:ps.address verificationCode:#""
withCallbackMethod:#selector(numberVerificationCallCompleted:)
callbackFailMethod:#selector(numberVerificationCallFailed:) onObject:self];
}
And the numberVerificationCallCompleted:
-(void)numberVerificationCallCompleted:(NSNumber*)responseNumber{...}
So, my question is, is this a thing with selectors. I mean, is that why they are used? And how does it work? The method still needs an argument to work properly, so it has to come from somewhere, right?
A selector describes a method within an Objective-C class, allowing you to call it at a later point in time. Parameters are passed when it's called, not when it's described.
You store selectors in a SEL type and call them using performSelector: making them ideal to use as callbacks.
The method you show above, will do something like this:
- (void)sendVerifyNumber:(NSString*)address
verificationCode:(NSString*)verificationNumber
withCallbackMethod:(SEL)callbackMethod
callbackFailMethod:(SEL)failedCallbackMethod
onObject:(id)callbackObject
{
BOOL success = doWhateverIDo();
if (success) {
[callbackObject performSelector:callbackMethod
withObject:someArgumentToPassback];
} else {
[callbackObject performSelector:failedCallbackMethod
withObject:someArgumentToPassback];
}
}
Note: you can only call a method that is described by a selector if it takes zero, one or two parameters (using performSelector:, performSelector:withObject: and performSelector:withObject:withObject: respectively).
I'm just beginning with ObjC. I'm wondering how to find out when looking at code, written by me or from a template that comes when you use the wizard to create a new class, how you can tell if a method is overriding something.
In Java, you can mark a method with #Override, and then it's very easy to see if it's overriding something. That's not foolproof, because #Override is optional, but if I'm still unsure I can just type that in and see if it generates an error.
Is the only way to look up the source of the superclass, or in the case of a framework to read the documentation?
I don't know a way to see this immediately, but you could check if super responds
to the same selector. Example:
- (void)myMethod
{
// Temporarily add this line. If the compiler does NOT complain,
// "myMethod" overrides a method from some superclass.
[super myMethod];
// ...
}
You can use instancesRespondToSelector to see if your instance has an implementation of the method in its object hierarchy.
[MyClass instancesRespondToSelector:#selector(myMethod)];
or depending on what type of checking you need to do
[MyClassSuperClass instancesRespondToSelector:#selector(myMethod)];
I'm taking a look at the developer library for iOS. A property called masterBirdSightingList was created with type NSMutableArray.Thats fine, in the implementation they added this peice of code
- (void)setMasterBirdSightingList:(NSMutableArray *)newList {
if (_masterBirdSightingList != newList) {
_masterBirdSightingList = [newList mutableCopy];
}
}
In order to
To implement a custom setter for the master list property ... (and) to override its default setter method to make sure that the new array remains mutable.
I'm not quite sure why this is fully necessary. Surely the array cannot suddenly change from a mutable array to something static. Is this method necessary?
Could you also help clarify whether the method setMasterBirdSightingList is called every time the masterBirdSightingList is set?
The tutorial I'm talking about is available here about halfway down the page.
Thank you
Could you also help clarify whether the method setMasterBirdSightingList is called every time the masterBirdSightingList is set?
Yes, it is - in Objective-C, property access is implemented using getter and setter methods. No exception. The dot notation is just syntactic sugar for - (T)foo and - (void)setFoo:(T)arg.
I'm not quite sure why this is fully necessary.
Because if you just declared a copy property, then upon setting the array, it would be sent the copy message, and that returns an immutable copy (in Cocoa [Touch], it's a common convention that copy returns an immutable copy, even if the original object was mutable.)
This is why explicitly sending mutableCopy to the argument is needed.
I'm new to programming in Cocoa, so I'm still struggling to grasp some basic concepts.
What I want to do (as an example) is write an application with multiple NSTextFields. However, these NSTextFields need to be linked to separate classes. Additionally, each separate class needs to be able to get and set data from each other.
I tried to add methods to tackle this problem, to no avail. Let's say this is a method in the textbox's original class, and I want to call it from another class.
-(void)settextfield:(NSString*)stringy;
{
[TextField setStringValue:stringy];
}
Here's the calling code (we're calling this from another class, TestClass)...
-(IBAction)test:sender;
{
[BundleBrowseTextBox settextfield: #"Testy"];
}
Nothing happens. There's probably some obvious way to do this, but I haven't been able to unearth this via Google searches.
My mistake was that I was calling the class method instead of the instance... you can call the instance via IBOutlets and defining those outlets properly in Interface Builder.
You need to make sure the pointers you are using are not nil.
One odd/convenient thing about objC is that you can pass messages to nil and it won't crash.
If I'm right in assuming you're trying to set the text in an instance of BundleBrowseTextBox, you should call the settextfield: message on the instance name, rather than on the class name (if BundleBrowseTextBox IS the instance -- rather than the class -- you should really avoid capitalized instance names for clarity). i.e.:
-(IBAction)test:(id)sender;
{
// Assuming bbtBox is defined as an instance of BundleBrowseTextBox
[bbtBox settextfield: #"Testy"];
}
I believe you forgot your parameter type in your original post
this...
-(IBAction)test:sender;
{
[BundleBrowseTextBox settextfield: #"Testy"];
}
should be
-(IBAction)test:(id)sender;
{
[BundleBrowseTextBox settextfield: #"Testy"];
}
That aside if you understand the difference between class and instance as you say you do.
Then it would be nice if you would show us the rest of your implementation and interface.
The problem is probably not in the code snippets you showed us.
I want to fix warnings in my application code. I have an AddressBookModel.h which implements the TTModel protocol.
You find both interface and implementation of the AdressBookModel in the answer of this question. This is exactly how I implemented it How to use Three20 TTMessageController?
However for
[_delegates perform:#selector(modelDidStartLoad:) withObject:self];
and some other similar selectors I get warnings like
Method -perform:withObject not found (return type defaults to id)
Since _delegates is an array
- (NSMutableArray*)delegates {
if (!_delegates) {
_delegates = TTCreateNonRetainingArray();
}
return _delegates;
}
some suggested to use makeObjectsPerformSelector but this gives me an unrecognized selector sent to instance exception.
Here is the TTModel source code: http://api.three20.info/protocol_t_t_model-p.php
Why is perform:withObject missing? Is performSelector:withObject an alternative (my app crashes using it)?
_delegates is an array of delegates. It is not a true delegate, as signified from the name which is in plural form. An array does not respond to the -modelDidFinishLoad: method — its elements do.
You need to take each element out of the array and call the method of them, e.g.
for (id<TTModelDelegate> delegate in _delegates)
[delegate modelDidFinishLoad:self];
or even easier, using NSArray's -makeObjectsPerformSelector:…:
[_delegates makeObjectsPerformSelector:#selector(modelDidFinishLoad:)
withObject:self];
perform:withObject: method that produces this warning is defined in NSArray(TTCategory) category in NSArrayAdditions.h file in Three20 framework. You need to ensure that this header is imported/referenced properly by compiler, i.e. you need to look at importing this specific header or check your Three20 integration configuration.
You do not need to change this method to makeObjectsPerformSelector: since this is just an import problem (your code runs fine but just produces compile warnings).
Reading between the lines, it looks like you want the objects that are in your _delegates array to all perform a particular selector. You need to call -makeObjectsPerformSelector:withObject: like this:
[_delegates makeObjectsPerformSelector: #selector(modelDidCancelLoad:) withObject: self];
You are mistyping modelDidCancleLoad: should be modelDidCancelLoad:
'NSInvalidArgumentException', reason:
'-[__NSCFArray modelDidCancleLoad:]: unrecognized selector
sent to instance 0x24f480'
Make sure your _delegates is what you are expecting it to be. It seems to be an NSArray.