Can non-alphanumeric characters be used as selectors? - objective-c

The following code compiles and runs fine (note the sel_registerName("+")):
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <objc/message.h>
#interface Integer : NSObject
{
NSInteger intValue;
}
#property (assign) NSInteger intValue;
#end
#implementation Integer
#synthesize intValue;
- (id) plus:(Integer*)anInteger
{
Integer* outInt = [Integer new];
[outInt setIntValue: intValue + [anInteger intValue]];
return outInt;
}
#end
int main (int argc, char const *argv[])
{
id pool = [[NSAutoreleasePool alloc] init];
SEL plusSel = sel_registerName("+");
Method m = class_getInstanceMethod([Integer class], #selector(plus:));
class_addMethod([Integer class], plusSel, method_getImplementation(m), method_getTypeEncoding(m));
Integer* i4 = [Integer new];
Integer* i20 = [Integer new];
[i4 setIntValue: 4];
[i20 setIntValue: 20];
Integer* res = objc_msgSend(i4, plusSel, i20);
NSLog(#"%d + %d = %d", [i4 intValue], [i20 intValue], [res intValue]);
// >> 4 + 20 = 24
[pool drain];
return 0;
}
Other than "yuck", are there reasons to be cautious about doing this?

The API to the ObjC runtime is unlikely to change, but the validity of calling sel_registerName("+") might. I've monkeyed around in the ObjC runtime a lot, and haven't run into any problems even after many updates. That being said, I wouldn't base a multimillion dollar business on this continuing to work forever.

Currently, the Objective-C runtime library doesn't perform any checks on the content of the string you are trying to register and it's unlikely that the development team change that behavior. If it is a non-empty C string, if you always use objc_msgSend to send messages for that selector and if you don't try to do something like [i4 +:i20] (which is going to cause a compiling error), there is no reason to be afraid.
Registered Objective-C selectors are actually C strings stored internally by the runtime system. The runtime system keeps a table of pointers to C strings, the so-called SEL set. When you call sel_registerName the ObjC runtime system calls strcmp for your string and for each C string stored in the SEL set. If any of the C strings in the SEL set is equal to the one you want to register, the function returns the address of the corresponding C string in the set. Otherwise, the system duplicates your string (with strdup), stores the resulting pointer in the SEL set and returns it. This new pointer becomes a new unique selector.

Related

Objective c — Update parameter in block

I was doing some tinkering with tree traversals (which I have solved in a much more straightforward way) but I have come across an issue in the following piece of Objective C logic:
- (NSString *)someWrapperFunction
{
NSString *result = #"";
NSString *(^appendBlock)(int, NSString **) = ^NSString *(int a, NSString **adder){
if (a == 0)
{
// base case
return #"";
}
NSLog(#"%d", a);
*adder = [*adder stringByAppendingFormat:#"-%d-", a];
NSLog(#"adder: %#", *adder);
return [*adder stringByAppendingString:appendBlock(a-1, adder)];
};
appendBlock(5, &result);
return result;
}
Basically, I want to create a block of code that concatenates numbers into the given string (adder). The result should be: "-5--4--3--2--1-".
I get a segmentation fault with the above code but with some other code that I wrote for the tree traversal, the adder string was essentially not getting updated. Any pointers to what I am doing wrong here? (Is it possible that the variable that is being updated by the inner block (inside recursion) is disallowed as it is already being occupied by the outer block OR is it just that NSString is non-mutable data type?)
In any case, I want to keep the design of the function the same; how would I solve this problem (using c/objective)?
After some searching and experimenting I found a way to fix this.
There is no reason to be using a double-pointer for your adder parameter in the block. Just use a regular pointer and update your code accordingly.
The error is coming from the fact that inside of the block, appendBlock is NULL and you end up dereferencing the NULL pointer trying to call it.
Here's an updated version that works:
- (NSString *)someWrapperFunction
{
NSString *result = #"";
NSString *(^appendBlock)(int, NSString *);
__block __weak NSString *(^weakBlock)(int, NSString *);
weakBlock = appendBlock = ^NSString *(int a, NSString *adder){
NSString *(^innerBlock)(int, NSString *) = weakBlock;
if (a == 0)
{
// base case
return #"";
}
NSLog(#"%d", a);
adder = [adder stringByAppendingFormat:#"-%d-", a];
NSLog(#"adder: %#", adder);
// Split this update to make it easier to debug.
NSString *update = innerBlock(a-1, adder);
return [adder stringByAppendingString:update];
};
appendBlock(5, result);
return result;
}
Output: "-5--4--3--2--1-"
This update is rewritten for point #1 (which really has nothing to do with your original issue.
To solve point #2 this update creates the original appendBlock variable as well as a new __block __weak weakBlock reference to the same block. And then inside the block, a new (strong) block pointer is created to reference the weak block pointer. Without the use of the weak pointer, the code works but causes a warning.

How to return string array in objective c

I am trying to get data from web service and adding it into nsmutablearray,
after that i want to return that array from extern c function.....
for example:
#implementation SampleClass
-(NSMutableArray* ) createArray:
{
NSMutableArray *array=[NSMutableArray new];
//
//add value in array
//
return array;
}
#end
extern 'C'
{
//how can i
NSArray* returnArray()// this method should return string array
{
SampleClass *sc=[[SampleClass alloc]init];
NSMUtableArray* a=[NSMutableArray new];
a=[sc createArray];
return a
}
}
#end
Why would you need to use extern "C" since Objective-C is a superset of C? You can already use C code without problems.
For your question it depends on lifetime of the array. If the array is retained then this is enough:
char *cString = [[array objectAtIndex:i] UTF8String];
Otherwise you'd need to allocate a new char* for each string, for example by using strdup:
char *cString = strdup([[array objectAtIndex:i] UTF8String]);
Mind that the latter will require you to release memory when you longer need it with free(cString). If you need to return a C array then you will create it before and set values accordingly, eg
char** cArray = calloc([array count], sizeof(char*));

Memory Management Objective-C

I don't understand why memory consumption increases and never gets released (the project is using ARC) when performing the following operations in my program (please bear with me, I'm at a basic level with plain C):
Simplified: somewhere in my program (AppDelegate for example) I call a macro which basically is a C function with variable parameters which calls other C functions that are returning some NSStrings.
These are defined and implemented in an Objective-C style class and are used together with a singleton object.
Header:
#interface MyClass : NSObject
#end
void func_1(aTypeDef paramType, NSString *input, ...);
void* func_2(NSString *arg1, NSString *arg2, NSString *arg3);
NSString* string_func_1 (void);
NSString* string_func_2 (int anInt);
NSString* string_func_3 (const char *aString);
#define F2_MACRO func_2( \
string_func_1(), \
string_func_2(anINT), \
string_func_3(aSTRING), \
)
#define F1_MACRO(input, ...) func_1(A_TYPE, input, ##__VA_ARGS__, F2_MACRO)
Implementation:
#import "MyClass.h"
static NSString *STRING_1;
static NSString *STRING_2;
static NSString *STRING_3;
#implementation MyClass
void func_1(aTypeDef paramType, NSString *input, ...) {
va_list args;
va_start(args, input);
NSString *output = [[NSString alloc] initWithFormat:input arguments:args];
fputs([output UTF8String], stdout);
va_end(args);
}
void* func_2(NSString *arg1, NSString *arg2, NSString *arg3) {
STRING_1 = arg1;
STRING_2 = arg2;
STRING_3 = arg3;
return NULL;
}
NSString* string_func_1 (void) {
return [NSString stringWithFormat:#"aString"];
}
NSString* string_func_2 (int anInt) {
return [NSString stringWithFormat:#"%d",anInt];
}
NSString* string_func_3 (const char *aString) {
return [NSString stringWithUTF8String:aString];
}
#end
Every time I call the F1_MACRO() in another Objective-C class like AppDelegate memory usage increases every time the string_func_1, string_func_2, string_func_3 return.
I'm sure that my logic and implementation are flawed and I'll appreciate any help.
func_1() is creating a non-autoreleased object.
• If you are using Automatic Reference Counting (ARC), not autoreleasing the object is fine, however it can still lead to apparent memory accretion. Specifically, if you don't have an explicitly #autoreleasepool{} or are not running an event loop on the thread that is calling that function, then the autoreleased object will never be released.
• If you aren't using ARC, then that is a straight up leak. Add [output release]; at the end of the func_1() function.

Objective-C: Comparing user input with object's instance variable

I have been learning Objective-C for a while, and I decided to try and take on a little bigger project without any real "guidelines" as in the learning books but now I have got stuck.
What I'm trying to do is helping a friend digitalising a few documents he have, by creating a searchable commandline-tool for these documents.
I think I have gotten pretty far, I have created a custom class for documents with three variable; name of the author, number of the article and the path to the file on my computer (which I of course will change to the place he have the documents stored on his computer). I have then created two example documents with all the variables filled in. Since the documents have two properties, numbers and name of the author, the user may search for one of these properties. I therefore separated the input of the user to either be a string or a int (with help of a stack overflow post: How to determine if the first character of a NSString is a letter ) I also created an array with the 'author'-variable's of the different documents.
This is were I have hit a bump: I want to run through the array of the 'author' and if the author's name match with what the user have put in, it will open the document which is at the path given at 'UrlToDoc'. The problem is, the instance variable 'UrlToDoc' is not "connected" to the 'leadAuthor'-variable in some kind of way (as far as I can tell). My question is therefore, how do I, after I have found a match in in the array with what the user written, describe the 'UrlToDoc'-variable for that specific object? (If the user typed in jamesson, for instance, how do I describe the UrlToDoc variable with the value: /Users/pinkRobot435/Desktop/test1.pdf )
Also, if the user writes in a number, the else-statement on the bottom (which would do the same thing) should be used. I haven't written it yet though, but I guess the code for it would be pretty much the same, when describing the 'UrlToDoc'-variable.
Here is my code:
My custom class SMADoc:
SMADoc.h
#import <Foundation/Foundation.h>
#interface SMADoc : NSObject
//Two strings, and a pathway to the documnt, with the purpose of describing the document
#property (nonatomic) int number;
#property (nonatomic) NSString *authour;
#property (nonatomic) NSString *urlToDoc;
#end
SMADoc.m
#import "SMADoc.h"
#implementation SMADoc
#end
main.m
#import <Foundation/Foundation.h>
#import "SMADoc.h"
#include <readline/readline.h>
#include <stdlib.h>
int main(int argc, const char * argv[]) {
#autoreleasepool {
SMADoc *one = [[SMADoc alloc] init];
[one setnumber:123];
[one setauthour:#"jamesson"];
[one setUrlToDoc:#"/Users/pinkRobot435/Desktop/test1.pdf"];
SMADoc *two = [[SMADoc alloc] init];
[two setnumber:124];
[two setauthour:#"marc"];
[two setUrlToDoc:#"/Users/pinkRobot435/Desktop/test2.pdf"];
NSMutableArray *authours = [[NSMutableArray alloc] initWithObjects: [one authour], [two authour], nil];
NSLog(#"Enter what you want to search for: ");
const char *searchC = readline(NULL);
NSString *searchOrg = [NSString stringWithUTF8String:searchC];
NSString *search = [searchOrg lowercaseString];
NSRange first = [search rangeOfComposedCharacterSequenceAtIndex:0];
NSRange match = [search rangeOfCharacterFromSet:[NSCharacterSet letterCharacterSet] options:0 range:first];
if (match.location != NSNotFound) {
//The string starts with a letter and the array of authour names should be searched through
for (SMADoc *nSearch in authours) {
if ([search isEqualToString:nSearch]) {
**//Open the file that is represented by UrlToDoc for that specific object**
} else {
NSLog(#"The authour was not found, please try again");
}
}
} else {
//The string starts with a number and should be converted to an int and then the array of numbers (which I have not yet created) should be searched through
int number = atoi(searchC);
}
}
return 0;
}
Thanks in avance!
Instead of your authours array, create an array of SMADoc objects.
Then, in your loop, each object from the array will have an authour or number you can match. When the right one is found, you can just pick the urlToDoc out of the same object.
Currently, you're calling each object in the array a SMADoc * when you examine it, but that's wrong. You created an array of NSString * (and you're comparing it as a string correctly) but what you need there is a real SMADoc *.

Get property name as a string

I need a way to pass a property and get the name assigned to it. Any suggestions?
#property (nonatomic, retain) MyObject *crazyObject;
NSString *str = SOME_WAY_TO_GET_PROPERTY_NAME(crazyObject);
// Above method should return #"crazyObject"
You can try this:
unsigned int propertyCount = 0;
objc_property_t * properties = class_copyPropertyList([self class], &propertyCount);
NSMutableArray * propertyNames = [NSMutableArray array];
for (unsigned int i = 0; i < propertyCount; ++i) {
objc_property_t property = properties[i];
const char * name = property_getName(property);
[propertyNames addObject:[NSString stringWithUTF8String:name]];
}
free(properties);
NSLog(#"Names: %#", propertyNames);
It's as simple as this...expanding upon what Chuck already mentioned:
#ifndef STR_PROP
#define STR_PROP( prop ) NSStringFromSelector(#selector(prop))
#endif
You then use it like so:
NSString *strProp = STR_PROP(myProperty);
Background
Keep in mind that properties are really just, to quote Apple, "a syntactical shorthand for declaring a class’s accessor methods." In fact, by itself, the #property declaration doesn't even work. Your #synthesize statement translates the #property into the equivalent of two methods:
- (void)setCrazyObject:(MyObject *)something;
- (MyObject *)crazyObject;
Which one is used depends on the context surrounding your self.crazyObject. (#synthesize also creates a matching instance variable if you didn't do it yourself.) The offshoot of all this is that you can't really translate to and from a property with one single method.
Proposed Solution
You can use what Apple already provides:
NSString *foo = NSStringFromSelector(#selector(myClassProperty));
Or do something custom:
Given that self.crazyObject really translates to either [self crazyObject] or [self setCrazyObject:foo] by the time your code is running, ou'll probably need two methods, like:
- (NSString *)setterStringForProperty:(SEL)prop;
- (NSString *)getterStringForProperty:(SEL)prop;
You might then want at least 2 companion methods such as:
- (SEL)setterForPropertyName:(NSString *)propString;
- (SEL)getterForPropertyName:(NSString *)propString;
Within these methods, you can use the Foundation functions NSStringFromSelector and NSSelectorFromString to convert back and forth between SEL and NSString. Use whatever string manipulations you like to convert back and forth between your setter string (setCrazyObject) and your property name (crazyObject).
A complete solution is hard to provide without knowing the exact use case, but hopefully this provides some more clues for anyone trying to accomplish something similar. There might even be some useful things made possible by combining this approach with Oscar's answer.
Here is a function that returns the name of an ivar, so basically it not only returns the properties but any ivar of the class. I haven't found a way to get the property directly so I used the ivar trick.
#import <objc/objc.h>
/// -----
- (NSString *)nameOfIvar:(id)ivarPtr
{
NSString *name = nil;
uint32_t ivarCount;
Ivar *ivars = class_copyIvarList([self class], &ivarCount);
if(ivars)
{
for(uint32_t i=0; i<ivarCount; i++)
{
Ivar ivar = ivars[i];
id pointer = object_getIvar(self, ivar);
if(pointer == ivarPtr)
{
name = [NSString stringWithUTF8String:ivar_getName(ivar)];
break;
}
}
free(ivars);
}
return name;
}
After searching and debugging i find solution for me...
Added #import <objc/runtime.h>
Methods object_getIvar(id obj, Ivar ivar) send bad access and app crashes. i modify some code and it worked great:
+(NSString*)stringWithProperty:(id)property withClass:(id)controller
{
NSString *name = nil;
uint32_t ivarCount;
Ivar *ivars = class_copyIvarList([controller class], &ivarCount);
if(ivars)
{
for(uint32_t i=0; i<ivarCount; i++)
{
Ivar ivar = ivars[i];
name = [NSString stringWithUTF8String:ivar_getName(ivar)];
if ([controller valueForKey:name] == property)
{
break;
}
}
free(ivars);
}
return name;
}
Modifying the solution, it works when your object is allocated already, otherwise it returns nil:-
NSString * NSStringFromProperty(NSObject* property, NSObject* class)
{
unsigned int propertyCount = 0;
objc_property_t * properties = class_copyPropertyList([class class], &propertyCount);
NSString *name = nil;
for (unsigned int i = 0; i < propertyCount; ++i)
{
name = [NSString stringWithUTF8String:property_getName(properties[i])];
NSObject *object = [class valueForKey:name];
if (object != nil && object == property)
{
break;
}
else
{
name = nil;
}
}
free(properties);
return name;
}
You can use
NSString *str = NSStringFromSelector(#selector(crazyObject));
The good thing about this approach is that:
Xcode will autocomplete word crazyObject for you.
When later on you will change the property name from crazyObject to myCrazyObject, Xcode will add a warning saying "unrecognized selector!" -- pretty good for debugging.
I use this method so often, that I even created a function, which allows to write less letters:
NSString * __nonnull sfs(SEL __nonnull theSelector)
{
if (!theSelector)
{
abort();
}
return NSStringFromSelector(theSelector);
}
Now your final solution can look like this:
NSString *str = sfs(#selector(crazyObject));
From Get property name as string, without using the runtime reference library, just define:
#define propertyKeyPath(property) (#""#property)
#define propertyKeyPathLastComponent(property) [[(#""#property) componentsSeparatedByString:#"."] lastObject]
And then you can do something like this:
NSLog(#"%#", propertyKeyPathLastComponent(appleStore.storeLocation.street)); //result: street
You may check my approach at Gist to get the string for a property with autocompletion and compile-time check.
How to use:
Get the property name for a class:
#interface AnyClass : NSObject
#property (strong) NSData *data;
#end
// == My approach ==
// C string for a class
PropertyNameForClass(AnyClass, data); // ==> "data"
// NSString for a class
PropertyStringForClass(AnyClass, data); // ==> #"data"
// Bad approach (no autocompletion; no compile-time check):
NSString *propertyName = #"data";
Get the property name for a protocol:
#protocol AnyProtocol
#property (strong) NSDate *date;
#end
// C string for a protocol
PropertyNameForProtocol(AnyProtocol, date); // ==> "date"
// NSString for a protocol
PropertyStringForProtocol(AnyProtocol, date); // ==> #"date"
Unconventional, hacky, ugly, late, but... as strong-named as it gets and works like a charm:
#define SOME_WAY_TO_GET_PROPERTY_NAME(p) p == p ? [[[[[[[NSString alloc] initWithCString:#p encoding:NSUTF8StringEncoding] componentsSeparatedByString:#"."] lastObject] componentsSeparatedByString:#" "] lastObject] stringByReplacingOccurrencesOfString:#"]" withString:#""] : #""
Sample usage:
NSLog(SOME_WAY_TO_GET_PROPERTY_NAME(self.customer.surname)); // surname
NSLog(SOME_WAY_TO_GET_PROPERTY_NAME([[self customer] birthDate])); // birthDate
...