excuse me. I want to use block as my property in category to change my code style as follows, but there is something wrong and I don't know why.
Here is my code :
```
typedef NSString* (^MethodreplacingRangeWithString)(NSRange range,NSString * string);
typedef NSString* (^MethodAppend)(NSString *) ;
#interface NSString (Speech)
#property(nonatomic ,copy)MethodreplacingRangeWithString replacingRangeWithString ;
#property(nonatomic, copy)MethodAppend append ;
+(void)speech:(NSString *)content;
#end
#implementation NSString (Speech)
//setter and getter
static NSString * a = #"replacingRangeWithString" ;
-(void)setReplacingRangeWithString:(MethodreplacingRangeWithString)replacingRangeWithString{
objc_setAssociatedObject(self, #selector(replacingRangeWithString), replacingRangeWithString, OBJC_ASSOCIATION_RETAIN_NONATOMIC) ;
}
-(MethodreplacingRangeWithString)replacingRangeWithString{
return objc_getAssociatedObject(self, #selector(replacingRangeWithString)) ;
}
//setter and getter
static NSString * b = #"append" ;
-(void)setAppend:(MethodAppend)append{
objc_setAssociatedObject(self, #selector(append), append,OBJC_ASSOCIATION_RETAIN_NONATOMIC) ;
}
-(MethodAppend)append{
return objc_getAssociatedObject(self, #selector(append)) ;
}
//block
-(void)configureReplacingRangeWithStringProperty{
__weak typeof (self) weakSelf = self ;
self.replacingRangeWithString = ^(NSRange range,NSString * str){
return [weakSelf stringByReplacingCharactersInRange:range withString:str];
};
}
-(void)configureAppend{
__weak typeof (self)weakSelf = self ;
self.append = ^(NSString *str){
return [weakSelf stringByAppendingString:str] ;
};
}
to change the style as follows :
NSString * str = #"hello world" ;
[str configureAppend] ;
[str configureReplacingRangeWithStringProperty] ;
str = str.replacingRangeWithString(NSMakeRange(6, 5),#"iOS").append(#" hhhhh") ;
```
here is sth wrong with my configures and I don't know why
The cause of the actual crash you are getting is probably down to a misunderstanding of associated objects. When you associate an object you do so to a specific instance, not to all instances of the same type.
Looking at your code:
[str configureAppend] ;
[str configureReplacingRangeWithStringProperty] ;
At this point you have associated two objects with the particular instance that str is referring to. Now you try to do:
str = str.replacingRangeWithString(NSMakeRange(6, 5),#"iOS").append(#" hhhhh") ;
Breaking this up:
str.replacingRangeWithString
This invokes the property replacingRangeWithString on whatever object instance str is referencing. Let's call that object A. Now your previous two statements associated objects with A, so this works and you get your block reference back.
str.replacingRangeWithString(NSMakeRange(6, 5),#"iOS")
This invokes the block and returns a different string, call that object B.
str.replacingRangeWithString(NSMakeRange(6, 5),#"iOS").append
This invokes the property append on object B, you have associated no objects with object B so this returns a null reference.
str.replacingRangeWithString(NSMakeRange(6, 5),#"iOS").append(#" ahhhh")
You attempt to call your "block", but you have a null reference => memory fault.
Update
At this point I originally suggested that you need to rethink your design - you do - and that a solution might not be as simple as you might like - and since then it dawned on me that there might be a simple approach...
Provided you are just trying to replace method calls with property accesses so you can neatly chain calls together; i.e. that you've no intention of calling your configure... methods with blocks which perform different operations; then you can dynamically create and return a block in your property. Here is the outline for append. First make the property read only:
#property(nonatomic, readonly) MethodAppend append;
and then define it using:
-(MethodAppend)append
{
NSString *copied = self.copy;
return ^(NSString *string){ return [copied stringByAppendingString:string]; };
}
This first copies the string in case the instance it is called on is an NSMutableString, you don't know how long after this property is invoked that the block will be called and the string value could have been changed by that point. Then it returns a block which accepts the required arguments and calls the intended method.
That is all that is required, no setters or configure methods, and no associate objects.
HTH
You declare the properties as copy and then implement strong setters.
More likely than not, your crash is because you have a block on the stack that is being used after the stack frame is destroyed.
Related
To my understand self refers to the current class and when i use a dot after self is to use one of its properties. In the code here there's a use in self.popOperand that i don't understand if popOpernad is not a property. Another thing i don't understand is why
[self pushOperand:result]; works and [self.pushOperand:result]; doesn't.
#import "Calcbrain.h"
#interface Calcbrain()
#property (nonatomic,strong) NSMutableArray *operandStack;
#end
#implementation Calcbrain
#synthesize operandStack = _operandStack;
-(NSMutableArray *) operandStack
{
if(_operandStack == nil) _operandStack = [[NSMutableArray alloc]init];
return _operandStack;
}
-(double)popOperand
{
NSNumber *objectNum = [self.operandStack lastObject];
if (objectNum)[self.operandStack removeLastObject];
return [objectNum doubleValue];
}
/*-(void) setOperandStack:(NSMutableArray *)operandStack
{
_operandStack = operandStack;
}*/
-(void)pushOperand:(double)opernand
{
[self.operandStack addObject:[NSNumber numberWithDouble:opernand]];
}
-(double)performOperation:(NSString *)operation
{
double result=0;
if([operation isEqualToString:#"+"])
{
result = self.popOperand + self.popOperand;
}
else if ([#"*" isEqualToString:operation])
{
result = self.popOperand * self.popOperand;
}
[self pushOperand:result];
return result;
}
#end
Whilst the . notation is primarily used for properties, it can be used for paramaterless methods that return a value. Why? Because the synthesised getter for a property is in the same form.
-(double)calcValue {
....
return value;
}
Is equivalent to the property declaration:
#property (nonatomic, readonly) double calcValue;
Whilst there may be no property declaration, it doesn't mean the . notation cannot be used. The compiler will effectively change . notation to a method call when compiling, as . is a form of syntactic sugar. As so:
self.popOperand
// Translates to
[self popOperand];
This leads on to part 2, why does [self.pushOperand:result]; not work? The reason being is that . does not support the passing of parameters directly.
The only way to assign/push a parameter to a property is via self.pushOperand = result, but this wouldn't work, because there isn't a corresponding - (void)setPushOperand:(double)pushOperand; that the . notation assignment maps to.
[self pushOperand:result]; works because you're being explicit in calling a particular method, called pushOperand:.
Overall, keep . notation for properties only, and if you're using a method that isn't designed to be a 'property', be explicit.
Update: self is a reserved keyword, that represents a pointer to the instance we're working within at that time.
For example, I can create two instances of Calcbrain outside of Calcbrain, for example BrainViewController:
Calcbrain* instance1;
Calcbrain* instance2;
Now, Calcbrain has methods declared within it, let's use -(double)performOperation:(NSString *)operation as an example. Now, if I wanted to call that from BrainViewController, I would do:
[instance1 performOperation:#"+"];
[instance2 performOperation:#"+"];
Because we are calling a method which is part of another class, I have to determine the correct instance I've created to refer to it (i.e. instance1 and instance2). But how would I call that from within the class itself, and make sure it applies to the correct instance? The instance I've created is unaware of the other instances I've created. Use self. self allows you to reference yourself within methods. So if I wanted to performOperation within Calcbrain itself, I would need to use self:
[self performOperation:#"+"];
The "dot" is just a syntactic sugar, it can be used even if there isn't a declared property. The expression
a.someProperty
is equivalent to
[a someProperty]
and the expression
a.someProperty = c
is equivalent to
[a setSomeProperty:c]
Therefore, self.popOperand is just the same as [self popOperand]. And one could also use the "dot" in some absurd cases like [NSMutableArray alloc].init.
Using the "dot" syntax for non-properties are highly discouraged. If I were the maintainer of this code I would change all self.popOperand back to [self popOperand] to avoid confusion.
(BTW, it is not defined which side of the + will get evaluated first. Better change
result = [self popOperand] + [self popOperand]
to
double operand1 = [self popOperand]
double operand2 = [self popOperand]
result = operand1 + operand2;
This will be a trouble when you define - and /.)
Dot is used to access properties of the class. You can also access properties and methods without dots:
[classInstance someMethodOrProperty];
This code: [classInstance someProperty]; equals this code classInstance.someProperty; You cannot call methods with dots like properties.
Obj-C blocks are something I'm just using for the first time recently. I'm trying to understand the following block syntax:
In the header file:
#property (nonatomic, copy) void (^completionBlock)(id obj, NSError *err);
In the main file:
-(void)something{
id rootObject = nil;
// do something so rootObject is hopefully not nil
if([self completionBlock])
[self completionBlock](rootObject, nil); // What is this syntax referred to as?
}
I appreciate the assistance!
Blocks are Objects.
In your case inside the method you are checking if the block is not nil and then you are calling it passing the two required arguments ...
Keep in mind that blocks are called in the same way a c function is ...
Below i have split the statement in two to let you understand better :
[self completionBlock] //The property getter is called to retrieve the block object
(rootObject, nil); //The two required arguments are passed to the block object calling it
Its a block property, you can set a block at runtime.
Here is the syntax to set
As it is void type, so within the class you can set a method by following code
self.completionBlock = ^(id aID, NSError *err){
//do something here using id aID and NSError err
};
With following code you can call the method/block set previously.
if([self completionBlock])//only a check to see if you have set it or not
{
[self completionBlock](aID, nil);//calling
}
How do I declare and use small helper functions inside my normal methods ?
In on of my objective-c methods I need a function to find an item within a string
-(void) Onlookjson:(id) sender{
NSString * res = [[sender gstring] copy];
persInfoBirth.text = getKeyValue(res, #"Birth");
}
I came up with a normal C type declaration for helper function getKeyvalue like this
NSString * getKeyvalue(NSString * s, NSString * key){
NSString *trm = [[s substringFromIndex:2] substringToIndex:[s length]-3];
NSArray *list = [trm componentsSeparatedByString:#";"];
//....
NSString res;
res = [list objectAtIndex:1];
//...
return res;
}
Example input string in s:
s=#"{ Birth = "1910"; Death = "1936"; }";
Anyway I get an exception "unrecognized selector sent to instance" for any of the two first lines in the helper function
How do I declare helper functions that are just to be used internally and how to call them safely ?
regards
Martin
Is this the real code? Do you get zero errors and warnings from the compiler? You must not ignore compiler warnings and you should turn on the Static Analyser in addition to the standard warnings.
There are many things wrong with the above code, most of which are nothing todo with declaring and calling methods. There is no way the above code could compile so maybe it pasted incorrectly or something..
Anyway.. declaring and using methods. Why are using a c function? Unless you have a good reason why not use Objective-c ? If you do have a good reason to use a C function the your definition should be:-
NSString *getKeyvalue( NSString *s, NSString *key ){
...
}
note the arguments. As NSString instances reside in the heap (not on the stack) you always want to pass pointers to them.
You then need to put the declaration in the header file:-
NSString *getKeyvalue( NSString *s, NSString *key )
EDIT:
In Objective-c there is no distinction between normal methods and helper methods, there is only one kind, and you have aleray written one
- (void)onLookJson:(id)sender { .. }
Taking it apart..
All methods begin with + or –, indicating Class method or Instance method. As you are familiar with C++ i guess you know what this means.
(void) is the return type. ie this method doesn't return a value. If it did it might look like (float) or (NSString *) or (id).
onLookJson: is the method name and the method takes 1 argument. Notice that the ':' is actually part of the name. This method is never is any circumstance just 'onLookJson'. An argument must always follow the :, so a method that doesn't take any arguments must not have one.
Ex
- (NSString *)fullName { .. }
This is an instance method, for example of a Person Class, you would call it like:-
NSString *theName = [aPerson fullName];
So
a method name that takes no
arguments is like 'speak'
a method
name that takes 1 argument is like
'speakTo:'
a method name that takes 2
arguments is like 'speakTo: language:'
a method name that takes 3
arguments is like 'speakTo: language: volume:'
etc.
All that is left is to put in the argument types and names.
Your function definition:
NSString *getKeyvalue( NSString *s, NSString *key ){
would become..
- (NSString *)getValue:(NSString *)s key:(NSString *)key { .. }
again, you need to declare it in the header or you will get a compiler warning.
- (NSString *)getValue:(NSString *)s key:(NSString *)key;
I'd like (at runtime) to bind a parameter to a function as you can do in boost::bind - a little like the following:
-(void)myFuncWithParameter:(NSString*)param {
NSLog(param);
}
-(void)init {
UIButton *helloButton = [UIButton buttonWithType:UIButtonTypeCustom];
[helloButton addTarget:self action:#selector(myFuncWithParameter:#"hello") forControlEvents:UIControlEventTouchUpInside];
}
So... I'm dynamically binding (at runtime) the value #"hello" to a parameter.
Obviously the above isn't the correct Syntax. Does anyone know if this is possible and the correct syntax?
Cheers,
Nick.
The short answer is no, or at least not at that level.
The long answer is that it is technically possible to build something akin to using NSInvocations (and/or forwardInvocation:), doing something clever in methodForSelector: and or by dynamically registering method implementations, but it is very tricky, especially if you care at all about speed.
If I had some code where building curried methods like that was really worthwhile, what I would do is something like this (written in this comment, untested);
//FIXME: In a real implementation you would do some mangling, this code will get confused if you have _s in the curried selector, and thus could be exploitable
//This method makes a unique selector by mangling the arguments
- (SEL) selectorForSelector:(SEL)bindSel withString:(NSString *)bindString {
NSString *mangle = [NSString *stringWithFormat:#"LGBind_%#_%#"], NSStringFromSelector(bindSel), bindString];
SEL retval = NSSelectorFromString(mangle);
//Register the imp. You probably want to check if it is already reg
if (![self respondsToSelector:retval]) {
class_addMethod([self class], retval, LGBind_IMP, "v#:")l
}
}
//Generic dispatcher imp
void LGBind_IMP(id self, SEL _cmd) {
NSString *selectorName = NSStringFromSelector(_cmd);
NSArray *array [selectorName componentsSeparatedByString:#"_"];
//Skip index 0; it is #"LGBind"
NSString *originalSelectorString = [array objectAtIndex:1];
NSString *originalArgString = [array objectAtIndex:2];
//Get our the SEL and the IMP
SEL originalSEL = NSSelectorFromString(originalSelectorString);
IMP originalIMP = [self methodForSelector:originalSEL];
//call the original imp
originalIMP([self class], originalSEL, originalArgString);
}
Obviously depending on your exact needs you could do things somewhere differently, for instance you could lazily by the imps in forwardInvocation, or stash data about the managled selector in a dict in the instance instead of just managling it into the selector name.
The general answer is that the target-action mechanism only allows for a target, a sender and a message that takes the sender; therefore, if you need to access data, you must get it from the target or the sender.
One option would be to create a class that represents the binding of a parameter value, a method and an object. This class would have an action that invokes the method on the object, passing the value. Use an instance of this class as the target. Here's a simplistic example:
#interface UnaryBinder : NSObject {
id target;
SEL selector;
id parameter;
}
#property id target;
#property SEL selector;
#property (retain) id parameter;
-(id)initWithTarget:(id)anObject selector:(SEL)aSelector param:(id)aParameter;
-(void)action:(id)sender;
#end
#implementation UnaryBinder
...
-(void)action:(id)sender {
[target performSelector:selector withObject:parameter];
}
#end
If you want to support an arbitrary number of parameters, you'd need to use NSInvocation (as Louis mentions) rather than performSelector:withObject. Of course, controls don't retain their targets, so you need some way of keeping the UnaryBinder around. At that point, you might as well skip the special class and just store the data in the control, as you mention in your comment about using KVP. Alternatively, factor out the action into a controller class and use an instance of that as the target. UnaryBinder and its ilk doesn't really offer any advantages when it comes to target-action. For related topics, google "higher order messaging".
I'm trying to build a NSArray of methods in Objective-C.
(What I'm trying to accomplish here is something like the following in C)
typedef (void)(*handler)(int command);
void handleCommandA(void) { ... }
void handleCommandB(void) { ... }
static const handler handler_table[10] = {
handleCommandA, handleCommandB, handleCommandC
};
I have to port this to Objective-C and I don't know how to
build an array of function pointers (in Objective-c world,
class methods) at compile-time.
In Objective-C I have the following.
- (void)handleCommandA { ... }
- (void)handleCommandB { ... }
/* Now how to add above 2 functions into NSArray? */
NSArray *handler_table = [NSArray arrayWithObjects:... ]; /* This doesn't seem to work. */
The problem here is that to bind those functions you must use the selector keyword which returns a SEL type. This is a pointer type whereas NSArray stores objects.
You thus have three options;
Use a regular C-type array
Fold the functions into an NSObject derived class that will call them.
Use a protocol.
The second is likely the nicer and for this you can use the NSValue class to hold the selector results. E.g;
NSValue* selCommandA = [NSValue valueWithPointer:#selector(handleCommandA:)];
NSValue* selCommandB = [NSValue valueWithPointer:#selector(handleCommandB:)];
NSArray *handler_table = [NSArray arrayWithObjects:selCommandA, selCommandB, nil ];
When you have retrieved the correct entry from the array, to convert back you would do;
SEL mySelector = [selCommand pointerValue];
[someObject performSelector:mySelector];
(Note I'm assuming that from your objective-c syntax that these are intended to be used as methods on an object and not global functions. If you wish to use them globally then you should write them as you would in plain C.)
Another option is to formalize the command methods into a protocol. This allows you to write functionality that will work on any object which implements that protocol and the compiler will provide more checking than if you were just calling selectors.
E.g.
// some header
#protocol CommandHandler
#required
-(void) handleCommandA;
-(void) handleCommandB;
#end
// some other header
#interface someClass : NSObject<CommandHandler>
{
// you will receive compiler warnings if you do not implement the protocol functions
}
Your handling and dispatch code is then written to work with objects of type "CommandHandler". E.g
-(void) registerForCommands:(CommandHandler*)handler
Use NSValue.
For example:
NSArray* handlers = [NSArray arrayWithObjects:[NSValue valueWithPointer:handleA] ... ];
then to access :
handleptr* handle = (handlerptr*)[[handlers objectAtIndex:0] pointerValue];
handle(foo_bar);
In Objective-C, you don't pass around methods; you pass around selectors, which are basically the canonical names of methods. Then, to make an object respond to a selector message, you send it performSelector:. For example:
NSString *exampleString = [NSString stringWithString:#"Hello"];
SEL methodName = #selector(stringByAppendingString:);
// ^This is the selector. Note that it just represents the name of a
// message, and doesn't specify any class or implementation
NSString *combinedString = [exampleString performSelector:methodName withObject:#" world!"];
What you'll want is to make an array of NSStrings containing the names of the selectors you're interested in. You can use the function NSStringFromSelector() to do this. Then, when you want to use them, call NSSelectorFromString() on the strings to get the original selector back and pass it to the appropriate object's performSelector:. (As shown in the example above, the receiver isn't encoded in a selector — just the method name — so you might need to store the receiver as well.)