Note, I'm specifically referring to the fact that dot notation is being used with class methods, not instance methods.
Out of curiosity, I wanted to see what would happen if I tried to use Objective-C dot notation syntax with a class method. My experiment was as follows:
#import <Foundation/Foundation.h>
static int _value = 8;
#interface Test : NSObject
+ (int) value;
+ (void) setValue:(int)value;
#end
#implementation Test
+ (int) value {
return _value;
}
+ (void) setValue:(int)value {
_value = value;
}
#end
int main(int argc, char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSLog(#"Test.value: %d", Test.value);
NSLog(#"[Test value]: %d", [Test value]);
Test.value = 20;
NSLog(#"Test.value: %d", Test.value);
NSLog(#"[Test value]: %d", [Test value]);
[Test setValue:30];
NSLog(#"Test.value: %d", Test.value);
NSLog(#"[Test value]: %d", [Test value]);
[pool release];
return 0;
}
I was surprised to see that this was compiled, let alone executed with what is, I suppose, correct behavior. Is this documented somewhere, or just a fluke of the compiler?
I compiled using GCC on Mac OS X 10.6:
gcc --version: i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5659)
compile using: gcc ObjCClassDotSyntax.m -framework Foundation -o ObjCClassDotSyntax
run: ./ObjCClassDotSyntax
output:
2010-03-03 17:33:07.342 test[33368:903] Test.value: 8
2010-03-03 17:33:07.346 test[33368:903] [Test value]: 8
2010-03-03 17:33:07.351 test[33368:903] Test.value: 20
2010-03-03 17:33:07.352 test[33368:903] [Test value]: 20
2010-03-03 17:33:07.353 test[33368:903] Test.value: 30
2010-03-03 17:33:07.353 test[33368:903] [Test value]: 30
This is correct behavior. foo.method is syntactic sugar for [foo method]—a straight conversion with identical semantics. Similarly foo.prop = bar is syntactic sugar for [foo setProp:bar], again with identical semantics. This transformation is implemented in the compiler. Thus you can use dot notation to call 0-parameter methods as in foo.doSomething instead of [foo doSomething]. Of course, if you do this, you are evil.
The fact that the callee is a class instance doesn't mater because in Objective-C, classes are also objects. Using dot notation on a class calls the parameterless method on that class.
Dot notation is described in the Objective-C Programming Language document.
In the "evil but it works" category, I've been known to use convenience constructors with the dot notation once in a while, such as NSMutableArray *myArray = NSMutableArray.array
The Underscore library further abuses this syntax by returning blocks from class methods, resulting in code like this:
NSArray *elements = Underscore.array(array)
.flatten
.uniq
.unwrap;
To understand how this works, look at the definition of Underscore.array:
+ (USArrayWrapper *(^)(NSArray *))array
{
return ^(NSArray *array) {
return [USArrayWrapper wrap:array];
};
}
So:
Underscore.array(array)
...is equivalent to this:
NSArray *array = #[];
USArrayWrapper * (^arr)(NSArray *) = [Underscore array];
USArrayWrapper *result = arr(array);
Related
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.
Let's say I have an uninitialized variable:
UIViewController *vc;
From this variable, I want to reference UIViewController, such that I could call alloc or new on it to return an instantiated object.
Essentially, I want to do:
UIViewController *vc = [*typeof(vc) new];
... which does not compile because the compiler expects an expression rather than a type.
If #encode returned the actual type, I could do something like:
UIViewController *vc = [NSClassFromString(#(#encode(*typeof(vc)))) new];
...however, #encode returns '#', which just means "generic object".
I realize it's a little philosophical in nature, but I get tired of typing and would like to make a macro like NEW(x). I also realize a similar macro could be made if it involves the actual declaration, but I am not satisfied with that syntax/angle.
Here's what I have... it doesn't seem ideal, since it makes a performance hit. Still looking for better answers.
static Class classFromEncoding(const char *encoding) {
char *className = strndup(encoding + 1, strchr(encoding, '=') - encoding - 1);
Class retval = NSClassFromString(#(className));
free(className);
return retval;
}
#define NEW(variable) [classFromEncoding(#encode(typeof(*variable))) new]
Here's a macro-only version:
#define CLASS_FROM_VARIABLE(variable) \
^(const char *encoding) { \
char *className = strndup(encoding + 1, strchr(encoding, '=') - encoding - 1); \
Class retval = NSClassFromString(#(className)); \
free(className); \
return retval; \
}(#encode(typeof(*variable)))
#define NEW(variable) [CLASS_FROM_VARIABLE(variable) new]
#define ALLOC(variable) [CLASS_FROM_VARIABLE(variable) alloc]
Variations can be made using objc_getClass() or NSString initWithBytes, perhaps with performance gains. Still, it's not a no-op, which is what I'd prefer.
It's [obj class] or [obj className] depending on your needs.
Heey
When I'm trying to return a array I'm always getting this strange "waring" message but it does not interrupt my App
Returning 'ABRecordRef' (aka 'const void *') from a function with result type 'ABRecordRef ' (aka 'const void *') discards qualifiers
Here is my code where I'm getting this message
- (ABRecordRef *) findContactsContainingName: (NSString *) fname
{
//TODO: add lastname, phonenumber etc.
// Load the contacts
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, nil);
NSArray *thePeople = (__bridge NSArray*)ABAddressBookCopyArrayOfAllPeople(addressBook);
for (id person in thePeople){
NSString *firstname = (__bridge NSString*) ABRecordCopyValue((__bridge ABRecordRef)(person), kABPersonFirstNameProperty);
if([firstname isEqualToString: fname]){
return (__bridge ABRecordRef)(person);
}
}
return NULL;
}
Can someone please explain me why I get here a Waring ..
Thanks for help and fast answer
Remove the * here:
- (ABRecordRef *) findContactsContainingName: (NSString *) fname
^
ABRecordRef is already defined as a pointer.
ABRecord is C API and it work in CoreFoundation ways.
In CoreFoundation (and AddressBook) objects are implemented as C structs, and pointers are used to reference them. A string in CoreFoundation is CFStringRef, which is interchangeable (or rather, toll-free bridged) with Foundation object, NSString *. (i.e. the "Ref" in CFStringRef implied a * in it - think it as CFString *, or rather struct __CFString *)
Similarly, ABRecordRef is ABRecord * and hence your return type, ABRecordRef * is actually ABRecord **, a secondary pointer. This is what the compiler is complaining.
You can check out the source code of GNUstep CoreBase and you will find out why. GNUstep is a open-source clone of Cocoa (it predates Cocoa!) for Linux and studying its source code can be very helpful on understanding how Cocoa work under the hood.
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.
Hee
Does anybody know how to implement an method in objective c that will take an array of arguments as parameter such as:
[NSArray arrayWithObjects:#"A",#"B",nil];
The method declaration for this method is:
+ (id)arrayWithObjects:(id)firstObj...
I can't seem to make such method on my own. I did the following:
+ (void) doSometing:(id)string manyTimes:(NSInteger)numberOfTimes;
[SomeClass doSometing:#"A",#"B",nil manyTimes:2];
It will give the warningtoo many arguments to function 'doSometing:manyTimes:'
Thanks already.
The ellipsis (...) is inherited from C; you can use it only as the final argument in a call (and you've missed out the relevant comma in your example). So in your case you'd probably want:
+ (void)doSomethingToObjects:(id)firstObject, ...;
or, if you want the count to be explicit and can think of a way of phrasing it well:
+ (void)doManyTimes:(NSInteger)numberOfTimes somethingToObjects:(id)firstObject, ...;
You can then use the normal C methods for dealing with ellipses, which reside in stdarg.h. There's a quick documentation of those here, example usage would be:
+ (void)doSomethingToObjects:(id)firstObject, ...
{
id object;
va_list argumentList;
va_start(argumentList, firstObject);
object = firstObject;
while(1)
{
if(!object) break; // we're using 'nil' as a list terminator
[self doSomethingToObject:object];
object = va_arg(argumentList, id);
}
va_end(argumentList);
}
EDIT: additions, in response to comments. You can't pass the various things handed to you in an ellipsis to another function that takes an ellipsis due to the way that C handles function calling (which is inherited by Objective-C, albeit not obviously so). Instead you tend to pass the va_list. E.g.
+ (NSString *)doThis:(SEL)selector makeStringOfThat:(NSString *)format, ...
{
// do this
[self performSelector:selector];
// make string of that...
// get the argument list
va_list argumentList;
va_start(argumentList, format);
// pass it verbatim to a suitable method provided by NSString
NSString *string = [[NSString alloc] initWithFormat:format arguments:argumentList];
// clean up
va_end(argumentList);
// and return, as per the synthetic example
return [string autorelease];
}
Multiple arguments (also known as an arglist) can only come at the end of a method declaration. Your doSomething method would look something like this:
+ (void)doNumberOfTimes:(NSInteger)numberOfTimes withStrings:(id)firstArg, ...
{
va_list args;
va_start(args, firstArg);
NSString * argString = firstArg;
while (argString != nil)
{
// do something with argString here
argString = va_arg(args, NSString *);
}
va_end(args);
}
To be called as follows:
[SomeClass doNumberOfTimes:2 withStrings:#"A", #"B", nil];
See also: How to create variable argument methods in Objective-C
I think you're after a variadic function. Here's Apple's documentation: http://developer.apple.com/library/mac/qa/qa2005/qa1405.html