This code gives me EXC_BAD_ACCESS, why?
NSMutableDictionary *d = [[NSMutableDictionary alloc] init];
IMP imp= [d methodForSelector:#selector(setObject:forKey:) ];
imp(d, #selector( setObject:forKey:), #"obj", #"key");
I'm just starting using IMP, firs try.. no luck. Not sure why I get the error, also.. in the past, when I got EXC_BAD_ACCESS, the message was printed at the console, this time the error line is highlighted.
Some notes:
ARC is enabled, XCode 4.3.2, the project uses Objective-C++ as de default language/compiler,this code is at the very beginning of the project
thanks guys
You need to cast the function pointer properly or ARC doesn't know what it's supposed to be doing. IMP is a generic function pointer that takes an id, a selector and a variable number of other, undefined arguments and returns an id. The method implementation you're trying to call takes an id, a selector followed by exactly two id parameters and has a void return type. You can fix it by changing to the following code:
NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
void (*imp)(id, SEL, id, id) = (void(*)(id,SEL,id,id))[dict methodForSelector:#selector(setObject:forKey:)];
if( imp ) imp(dict, #selector(setObject:forKey:), #"obj", #"key");
You should always check that you actually got a function pointer back before you dereference it, as that would also crash. The code above will work even in an ARC environment. Also, even when you're not using ARC, you should always cast your function pointers to the actual prototype rather than IMP. You should never use IMP. Other places that would cause major issues are if the method returns a struct or if the method takes floating point parameters, etc.
Good habit: always cast your function pointers or make typedefs for them if you find the function pointer syntax jarring.
The problem is that IMP has a return type of "id" which ARC will attempt to manage. You need to cast your function pointer to have a return type of void (consistent with the method you are calling):
NSMutableDictionary *d = [[NSMutableDictionary alloc] init];
IMP imp= [d methodForSelector: #selector(setObject:forKey:)];
void (*func)(__strong id,SEL,...) = (void (*)(__strong id, SEL, ...))imp;
func( d, #selector(setObject:forKey:), #"obj", #"key" );
Related
I have the following code:
SEL moveAlongBoth = #selector(moveAlongX:andY:);
if ([p1 respondsToSelector:moveAlongBoth]) {
[p1 performSelector: moveAlongBoth
withObject: [NSNumber numberWithInt:1]
withObject: [NSNumber numberWithInt:1]];
}
I am getting a "performSelector may cause leak" warning.
But
[p1 moveAlongX:1 andY:1];
Works just fine.
I understand that I am getting the error because the values are set to (int) in the implementation and I am using NSNumber. Without changing the implementation, how would I go about declaring a number value to int (if possible)?
Why can't you just do this:
if ([p1 respondsToSelector:#selector(moveAlongX:andY:)]) {
[(id)p1 moveAlongX:1 andY:1];
}
By the way, the Cocoa naming convention would have you call this method moveAlongX:y:.
With regard to the second part. If you are in charge of the classes that may be the type of p1, then you could define a protocol with moveAlongBoth:: and instead of checking with performSelector check wit conformsToProtocol. Let's say the protocol's name is CanMoveAlong then you can cast it to
id <CanMoveAlong> canDo = (id<CanMoveAlong>)p1;
after you checked conformity and directly invoke the method
[canDo moveAlongX:1 andY:1];
Doing so you achieve both, you get rid of the warning and you can pass int directly without using NSNumber.
I'm trying to implement the countByEnumeratingWithState:objects:count: method from the NSFastEnumeration protocol on a custom class.
So far I have it iterating through my objects correctly, but the objects that are returned aren't Objective-C objects but rather the core foundation equivalents.
Here's the part of the code that sets the state->itemsPtr:
MyCustomCollection.m
- (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState *)state
objects: (id __unsafe_unretained *)buffer
count: (NSUInteger)bufferSize {
// ... skip details ...
NSLog(#"Object inside method: %#", someObject);
state->itemsPtr = (__unsafe_unretained id *)(__bridge void *)someObject;
// ... skip details ...
}
Then I call the 'for..in' loop somewhere else on like this
SomeOtherClass.m
MyCustomCollection *myCustomCollection = [MyCustomCollection new];
[myCustomCollection addObject:#"foo"];
for (id object in myCustomCollection) {
NSLog(#"Object in loop: %#", object);
}
The console output is:
Object inside method: foo
Object in loop: __NSCFConstantString
As you can see, inside the NSFastEnumeration protocol method the object prints fine, but as soon as it gets cast to id __unsafe_unretained * I lose the original Objective-C corresponding class.
To be honest I'm not quite sure how the (__unsafe_unretained id *)(__bridge void *) casting works in this case. The (__unsafe_unretained id *) seems to cast to match the right type itemsPtr needs. The (__bridge void *) seems to cast to a pointer of type void with __bridge used to bridge the obj-c world to the CF world. As per the llvm docs, for __bridge:
There is no transfer of ownership, and ARC inserts no retain operations
Is that correct?
From my understanding __NSCFConstantString is just the core foundation equivalent of NSString. I also understand that with ARC you need to bridge from Objective-C objects to CoreFoundation equivalents because ARC doesn't know how to manage the memory of the latter.
How can I get this working so that the objects in my 'for..in' loop are of the original type?
Also note that in this case I'm adding NSStrings to my collection but in theory it should support any object.
UPDATE
Rob's answer is on the right track, but to test that theory I changed the for loop to this:
for (id object in myCustomCollection) {
NSString *stringObject = (NSString *)object;
NSLog(#"String %# length: %d", stringObject, [stringObject length]);
}
In theory that should work since the objects are equivalent but it crashes with this error:
+[__NSCFConstantString length]: unrecognized selector sent to class
It almost looks like the objects returned in the for loop are classes and not instances. Something else might be wrong here... Any thoughts on this?
UPDATE 2 : SOLUTION
It's as simple as this: (thanks to CodaFi
state->itemsPtr = &someObject;
You're incorrectly casting someObject. What you meant is:
state->itemsPtr = (__unsafe_unretained id *)(__bridge void *)&someObject;
(Let's get rid of those awful casts as well)
state->itemsPtr = &someObject;
Without the address-of, your variable is shoved into the first pointer, which is dereferenced in the loop. When it's dereferenced (basically, *id), you get the underlying objc_object's isa class pointer rather than an object. That's why the debugger prints the string's value inside the enumerator call, and the class of the object inside the loop, and why sending a message to the resulting pointer throws an exception.
Your code is fine the way it is. Your debug output is revealing an implementation detail.
NSString is toll-free-bridged with CFString. This means that you can treat any NSString as a CFString, or vice versa, simply by casting the pointer to the other type.
In fact, under the hood, compile-time constant strings are instances of the type __NSCFConstantString, which is what you're seeing.
If you put #"hello" in your source code, the compiler treats it as a NSString * and compiles it into an instance of __NSCFConstantString.
If you put CFSTR("hello") in your source code, the compiler treats it as a CFStringRef and compiles it into an instance of __NSCFConstantString.
At run-time, there is no difference between these objects in memory, even though you used different syntax to create them in your source code.
If I have an object that is already allocated, then doing object.class returns a non-nil value. So far so good. But, if the object has not yet been allocated, then accessing object.class returns nil.
I want to allocate an object based on its type dynamically, so for example:
#property NSArray *myArray;
...
// myArray is nil so far
self.myArray = [_myArray.class new];
However, I can't do this because _myArray.class is returning nil. So how would I determine the class type of a nil instance?
Update:
It is in fact possible. Check out my answer below.
You cannot determine the class of a nil instance, because it does not have one: it can be, quite literally, of any type derived from the type of the variable. For example, NSMutableArray is perfectly compatible with NSArray:
NSArray *myArray = [NSArray new]; // OK
NSArray *myArray = [NSMutableArray new]; // Also OK
Since the run-time capabilities of different subclasses can vary a lot, it is always up to your program to decide what kind of objects it wants.
Objective-C is a duck-typed language. This means that there are several things you can or can't do, and one of the things you can't is statically get a reference to the type of a variable.
Specifically, in your expression:
[_myArray.class new]
First, _myArray.class is evaluated, and then the result is sent the new message. Since _myArray is nil to begin with, _myArray.class returns nil as well, and the new message will return nil too, because sending any message to nil returns nil (or the closest representation to zero the return type has). This is why it doesn't work.
I suspect you come from a strongly-typed language like C#; what you're doing right now is the equivalent of Foo foo = (Foo)Activator.CreateInstance(foo.GetType()), which is sure to fail because foo.GetType() will either not compile or throw an exception (depending on if it's a class field or a local variable) since it was never assigned a value. In Objective-C, it compiles but it doesn't works. What you would want is Activator.CreateInstance(typeof(Foo)), but notice that Foo is now hardcoded here too, so you might as well just create a new Foo().
You say that the compiler "knows the type" of the object. This is not exactly true. First, NSArray and NSMutableArray are the root classes of the NSArray class cluster. This means that both are abstract, and [NSArray alloc] and [NSMutableArray alloc] return an instance of a subclass (NSCFArray last time I checked, and possibly something else; I recall seeing _NSArrayM). Maybe [NSArray new] works, but it's not giving you a plain NSArray.
Second, type safety is not enforced. Consider this code:
id foo = #"foo";
NSArray* bar = foo; // no warning!
So even though the compiler thinks that bar is an NSArray, it's in fact a NSString. If we plug in your code:
id foo = #"foo";
NSArray* bar = foo; // no warning!
NSArray* baz = [bar.class new];
baz is now an NSString as well. Since you ask for the runtime class of bar, the compiler has nothing to do with the operations.
And precisely because of that kind of behavior, you should probably instantiate your object with a class that you know, using [NSArray new] instead of trusting _myArray to be non-nil, and to be what you think it is.
You must init the property , or it will be nil , send a message to a nil object , it will return nil , so ,you must first init the array like _array = [[NSArray alloc] init];
So, for anyone wondering if this is possible, it is:
objc_property_t property = class_getProperty(self.class, "myArray");
const char * const attrString = property_getAttributes(property);
const char *typeString = attrString + 1;
const char *next = NSGetSizeAndAlignment(typeString, NULL, NULL);
const char *className = typeString + 2;
next = strchr(className, '"');
size_t classNameLength = next - className;
char trimmedName[classNameLength + 1];
strncpy(trimmedName, className, classNameLength);
trimmedName[classNameLength] = '\0';
Class objectClass = objc_getClass(trimmedName);
NSLog(#"%#", objectClass);
Output:
NSArray
Done with the help of extobjc.
Nil has no class type
In Objective-C the actual class on an instance variable is only determined at runtime. So, you can't know the class of a nil object.
This is not an issue in your situation since you only need to do:
NSArray *myArray = [NSArray new];
Or
NSArray *myArray = [[NSArray alloc] init];
In Objective-C most decisions are deferred to the runtime
(as much as possible)
Objective-C is a runtime oriented language, which means that when it's
possible it defers decisions about what will actually be executed from
compile & link time to when it's actually executing on the runtime.
This gives you a lot of flexibility in that you can redirect messages
to appropriate objects as you need to or you can even intentionally
swap method implementations, etc.
This requires the use of a runtime
which can introspect objects to see what they do & don't respond to
and dispatch methods appropriately. If we contrast this to a language
like C. In C you start out with a main() method and then from there
it's pretty much a top down design of following your logic and
executing functions as you've written your code. A C struct can't
forward requests to perform a function onto other targets.
Source: Understanding the Objective-C Runtime
I wrote an action that fires when any objc method return (objc:::return).
For now, I need to get the return value. Is it possible?
In summary: no, you can’t get the return value of an Objective-C method in a DTrace probe.
Bill Bumgarner has a post on tracing messages to nil in which he says the following:
Aside: objc_msgSend()‘s return cannot be traced via dtrace at this time. Not surprising given that the function doesn’t actually return — as far as dtrace is concerned, it is nothing but preamble.
The blog post is rather old (January 2008) and it uses the pid provider, not the objc provider. That said, it is still valid as of Mac OS X v10.7.1 and it also applies to the objc provider.
Amusingly, it might seem that it sometimes works but it depends on when DTrace reads the RAX register. Since objc_msgSend() doesn’t return, DTrace ends up using values in RAX that have been stored by code that is not necessarily the return of the method being traced.
Consider the following code:
NSNumber *n = [NSNumber numberWithInt:1234];
printf("n has address %p\n", n);
and the following probe:
objc$target:NSPlaceholderNumber:-initWithInt?:return
{
printf("Returning from -[NSPlaceholderNumber initWithInt:]\n");
printf("\treturn value = 0x%p\n", (void *)arg1);
}
When run using DTrace, I get the following output:
n has address 0x4d283
Returning from -[NSPlaceholderNumber initWithInt:]
return value = 0x4d283
so it seems that the probe was able to capture the return value of -initWithInt:. That’s only luck though, probably caused by a function (e.g. CFNumberCreate() or CFMakeCollectable()) called by -initWithInt: and which ended up placing the expected value in RAX.
Now consider the following code:
char *c = "hello";
NSData *data = [NSData dataWithBytes:c length:strlen(c)];
NSString *s = [[NSString alloc] initWithData:data
encoding:NSASCIIStringEncoding];
printf("s has address %p\n", s);
and the following probe:
objc$target:NSPlaceholderString:-initWithData?encoding?:return
{
printf("Returning from -[NSPlaceholderString initWithData:encoding:]\n");
printf("\treturn value = 0x%p\n", (void *)arg1);
}
When run using DTrace, I get the following output:
s has address 0x7fcd92414ea0
Returning from -[NSPlaceholderString initWithData:encoding:]
return value = 0x600
As you can see, the addresses (i.e., the return values) don’t match. In fact, 0x600 is the value of kCFStringEncodingASCII which is the Core Foundation counterpart of NSASCIIStringEncoding. At some point either the method or a function called by the method moved 0x600 to RAX and that’s the value that DTrace wrongly considered to be the return value.
- (void)createAString:(NSString **)str
{
*str = [NSString stringWithString:#"Hi all!"];
[*str autorelease]; // ???? is this right ?
}
How should I use release or autorelease ? I don't want to release outside of the function of course :)
...
NSString *createStr;
[self createAString:&createStr];
NSLog(#"%#", createStr);
You're correct that you'd generally want to return autoreleased (or the like) objects from out params when you use this form. Your assignment statement in the function that sets *str to a string:
*str = [NSString stringWithString:#"foo"];
is already doing the right thing, because that method returns an instance of NSString that the caller doesn't own. Just like you could return this string object from your function without any further memory management, you can set it as the outparam as you've done. Your second snippet showing the call site is fine.
This said, I'm worried about a few things in your code that you should be sure you understand:
The value of str inside the method is still a **, and sending that a message (as you've done for the speculative autorelease) is nonsense. Be sure you fully understand doubly indirected pointers before using them too liberally. :) If you need to send str a message after creating it, send it to *str, which is what contains the NSString *.
Setting an outparam like this when the function returns void is not idiomatic Cocoa. You would normally just return the NSString * directly. Outparams are rare in Cocoa. (Usually just NSErrors get this treatment from framework calls. Otherwise they conventionally use name like getString to differentiate them from normal get accessors which don't use the word "get".)
I hope -stringWithString was just an example. That method is almost never used in practice, since it's equivalent (in this case) to just using a #"string literal" (although that would muddy your example).
Instead of using a double pointer, would it not be more elegant to use an NSMutableString instead?
- (void)createAString:(NSMutableString *)str
{
[str setString:#"Hi all!"];
}
....
NSMutableString *createStr = [[NSMutableString alloc] init];
[self createAString: createStr];
NSLog(#"%#", createStr);
[createStr release];
Or, even better, just have the createAString method return an NSString.
- (NSString *)createAString
{
return #"Hi all!"; // this is autoreleased automatically
}
I wouldn't want to presume that your needs are this simple, though. =)