Does CFRetain keep a bridged ObjC object alive? - objective-c

I'm using ARC and I have an object whose reference must be passed to a struct:
myStruct->myObject = (__bridge void *)self;
There are cases where all standard references other than the one in this struct will have passed out of scope, but I still want the struct to keep the object's retain count from hitting 0.
Can I just do this? :
CFRetain(myStruct->myObject);
and then later when I'm destroying my struct just call
CFRelease(myStruct->myObject);
These are getting called on the void* bridged reference, and I'm not sure if they keep that actual Objective-C class alive.

Yes, that works. Note that you can also write
myStruct->myObject = CFBridgingRetain(self);
to cast the Objective-C object to a const void * and "take ownership".

Related

Send the message objc_msgSend(class,#selector(dealloc)) to release the object, why is it wrong to access the object pointer?

The code is under ARC. When I delete the code NSObject* objc = (NSObject*)object; the program runs fine, but I didn't have access to the pointer objc. When I keep the code NSObject* objc = (NSObject*)object; I am prompted EXC_BAD_ACCESS (code=1, address=0x20). Is the system accessing the objc pointer after the block function body ends?
-(void)resetDeallocMethodWithInstance:(NSObject*)obj
{
Class targetClass = obj.class;
#synchronized (swizzledClasses()) {
NSString *className = NSStringFromClass(obj.class);
if ([swizzledClasses() containsObject:className]) return;
SEL deallocSel = sel_registerName("dealloc");
__block void (*deallocBlock)(__unsafe_unretained id, SEL) = NULL;
id block = ^(__unsafe_unretained id object){
NSObject* objc = (NSObject*)object;
NSUInteger hash = ((NSObject*)object).hash;
[self removeAllTargetWitSuffixKey:[NSString stringWithFormat:#"%lu",(unsigned long)hash]];
if (deallocBlock == NULL) {
struct objc_super superInfo = {
.receiver = object,
.super_class = class_getSuperclass(targetClass)
};
void (*msgSend)(struct objc_super *, SEL) = (__typeof__(msgSend))objc_msgSendSuper;
msgSend(&superInfo, deallocSel);
} else {
deallocBlock(object, deallocSel);
}
};
IMP blockImp = imp_implementationWithBlock(block);
if (!class_addMethod(obj.class, deallocSel, blockImp, "v#:")) {
Method deallocMethod = class_getInstanceMethod(obj.class, deallocSel);
deallocBlock = (__typeof__(deallocBlock))method_getImplementation(deallocMethod);
deallocBlock = (__typeof__(deallocBlock))method_setImplementation(deallocMethod, blockImp);
}
[swizzledClasses() addObject:className];
}
return;
}
enter image description here
Note: This answer is being directly typed in, your code has not been tested, indeed no code has been tested. Therefore that the issues below are causing your issues is being inferred.
There area number of issues with your design:
Swizzling dealloc is not recommended. The dealloc method is called automatically by the system when it is in the process of destroying an object, as such using the partly destroyed object inappropriately (whatever that might be) could lead to issues - as you have found!
You are using ARC under which "an implementation of dealloc, [should] not invoke the superclass’s implementation". However your block does this.
The variable objc is unused. However by default a local variable has the attribute strong so you are creating a strong reference to an object in the process of destruction. Any strong reference made by the block in this way will be released by ARC when the block has finished, this is almost certainly not good as your error indicates.
You appear to be trying to call your removeAllTargetWithSuffixKey: method when a particular object is destroyed (appear as you swizzle [and can only swizzle] the class but are using the hash of a particular object). A better way to do this avoiding swizzling is to use associated objects.
The runtime function objc_setassociatedobject() allows you to attach an object to a particular instance of another object and have that object be destroyed automatically when its host is destroyed (use an objc_AssociationPolicy of OBJC_ASSOCIATION_RETAIN).
Design a class which has an instance property of your required hash value and a dealloc method which calls your removeAllTargetWithSuffixKey: then rather than swizzle the class simply create and associate an instance of your class with the target object.
HTH
Yes, it's accessing the pointer after the method ends. If this is being compiled under ARC, then the objc is a "strong" reference. However, you are fabricating the implementation of the dealloc method, and so are retaining the object when it's already going to be dealloced, so it's too late to have a strong reference to it. Your implementation is going to call super, which should actually deallocate the object, and then afterwards ARC is going to release the objc value, but it's already gone since it's the receiver, i.e. "self" if you were writing a normal dealloc method.
ARC will never retain self in a regular dealloc method, but that is what you are effectively doing. The "object" value is the same pointer, but is explicitly __unsafe_unretained, so you should just use that directly. You can type the block as NSObject* instead of id if that helps, but it shouldn't matter. Or you can make your objc value also __unsafe_unretained so ARC leaves it alone. You don't want ARC touching the "self" value inside the block in any way, since you are going around ARC's back in this case.
Whatever the case, once you are in an object's dealloc method, don't ever retain/release/autorelease the self pointer -- it will end up with crashes. Calling a method from dealloc and passing a reference to self is a no-no, for example. You need to be very careful about that, and understand exactly what ARC is doing if you are playing these types of runtime games.

NSFastEnumeration object casting in ARC

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.

What does the const specifier do, before a pointer to object?

If I have a C-string like this:
const char* str= "hello";
I know well that I can't change any character of the string because the pointer is const.
But if I have this:
- (CLLocationDistance)distanceFromLocation:(const CLLocation *)location
I can still mutate the object state using it's methods.
If the pointer is to a NSMutableString, I'm still able to mutate it.
Then what does the const stand for?
In that method declaration, location is a pointer to a constant CLLocation. But when you send a message to the location object, the const-ness is not preserved; the method that handles the message sees self as a non-const object. (Note that this is different than C++, which supports const member functions where this is a pointer to a constant object.)
So the const in that declaration is not particularly useful. Perhaps it was written by someone used to the C++ way of doing things.
When you do see const attached to an object pointer in Objective-C, it is usually like this:
extern NSString * const SomeConstantString;
This declares SomeConstantString as a constant pointer to some non-constant object (in this case, an NSString). The pointer itself is constant, so your program can't change SomeConstantString to point to some other NSString object.
I know well that I can't change any character of the string because
the pointer is const.
No, the pointer is mutable. The characters it points to are const.
I can still mutate the object state using it's methods.
There is no const-correctness for Objective-C objects like there is in C++. The compiler does not care which messages (mutating or not) you send to a const object. So there's no sense in declaring a pointer to a const object. The cited framework method is an anomaly, probably an oversight.
Mind the difference:
// constant pointer
char * const str = "";
// pointer to constant (two equivalent ways)
const char * str = "";
char const * str = "";
The keyword const applies applies to whatever is immediately to its left. If there is nothing to its left, it applies to whatever is immediately to its right.
In Objective-C all method parameters are always passed by value. This includes primitives, structs, unions, and pointers, and any other made up type.
Note that you can't have variables of type object. A expression like NSObject o; produces a compiler error with message "Interface type cannot be statically allocated".
The only way to pass an object is passing a pointer. The pointer is passed as value, but lets the code inside the method reference the object and change it. So in a way, it is as if you are passing the object by reference (in reality you are passing the pointer by value).
When compiling an Objective-C program, the methods are turned into C functions, and each "message send" (aka "method call", though it isn't exactly the same) is ran using the runtime function objc_sendMsg. This function doesn't know or care if you qualified the object with const or not. If you want an immutable object, you have to code that immutability inside the object. Example:
// const qualifying an object is ignored whether in variables or method arguments:
const NSMutableArray *array = [NSMutableArray new]; // const is ignored
-(void)someMethod:(const NSMutableArray *)array { ... // const is ignored
// calling the methods that mutate the object works fine
[array removeAllObjects];

pointer pointer in objective-c

In c++ i would do the following,
Object* obj1;
Object* obj2;
Object** targetObj;
void SetTargetToObj1()
{
targetObj = &obj1;
}
void SetTargetToObj2()
{
targetObj = &obj2;
}
void ValueChanged()
{
//So if SetTargetToObj2() was called before ValueChanged() we
// would be changing some data on obj2
(*targetObj)->ChangeSomeData();
//or, we obj2 is null we could assign a new object to it via targetObj
(*targetObject) = new Object();
//now obj2 is pointing to our new object
}
Im wondering if there is a way in obj-c to do this same thing with NSObjects?
Pointers to pointers are not so simple under ARC.
When you declare, say, an instance variable:
NSObject *someObject;
you are implicitly declaring:
NSObject * __strong someObject;
i.e. a strong pointer. Strong is just one of the ownership qualifiers, you can also have weak and autoreleasing qualifiers.
Now taking the example in your comment:
NSDate **targetDate;
you get the error "pointer to non-const type 'NSDate *' with no explicit ownership". This is because ARC needs to know the ownership qualification of the pointer your pointer is referring to (read it slowly! ;-)). i.e ARC is asking you to type the variable instead as:
NSData * 'some ownership qualifer' * targetDate;
which, once you've decoded C's type priority rules, is a "pointer to a 'some ownership qualifier' pointer to an NSDate".
The error message includes "non-const" as this is all about writing via your pointer to pointer - ARC still needs to know how to handle the store, which depends on whether the pointed at reference is strong, weak, etc.
In your simple case the following should do:
NSObject *obj1;
NSObject *obj2;
NSObject * __strong * targetObj;
and then when doing (*targetObj) = ... etc. ARC knows what to do for memory management - which in this case is to release the old value in the variable referenced by targetObj as well as assigning the new reference into that variable.
Essential reading is Automatic Reference Counting and Transitioning to ARC Release Notes - in particular look up NSError in the latter as it explains how the common pattern of declaring error parameters as NSError ** is handled under ARC.
The code you have right there is already fine. If Object is in fact an obj-c object then this exact code is what you want. The only quirk is potential memory management issues (e.g. does targetObj need to retain the thing it's pointing to?)

How do I know if a pointer is an NSObject?

I have a pointer in an objective-C class that I need to send messages to. The pointer can potentially be anything, so I need to make sure that it will respond to my messages before I send them. Here's the function I'm using to do the checking:
int delegatePreparedForSelector(id delegate, SEL aSelector) {
if (delegate
&& [delegate isKindOfClass:[NSObject class]]
&& [delegate respondsToSelector:aSelector]) {
return YES;
}
return NO;
}
The problem is that sometimes the delegate pointer is a struct objc-object * and I get a EXC_BAD_ACCESS bad access error when i send the isKindOfClass message.
Is there a better test i should be using to determine if the delegate will respond to my messages?
Wait, do you really mean the pointer can be anything? Like a void * pointing to a chunk of raw malloc'ed memory, or an objc_object that does not derive from NSObject? If that is really the case then there is no way to make this work safely. It is equivalent to saying "Without dereferencing this pointer how can I know it is safe to dereference?" The only way is to have a priori knowledge that whatever passed it into you did not hand you a bad pointer.
You can try to write some signal handler code to cleanup an EXEC_BAD_ACCESS, but ultimately it will work slowly, poorly, and mask lots of other real bugs. Realistically you either have some constraints on what you are being passed in, or you need to re-architect this part of your project.
It sounds like your delegate is being disposed of before the call, not that there is anything necessarily wrong with this code.
Also, you can enforce a protocol implementation on the parameter like so: id<MyDelegateProtocol> delegate instead of just using a bare id.
the delegate pointer pointing to type struct objc_object causing a problem is confusing, as all objects in Obj-C are of type (digging into an obj-c object):
struct objc_object
{
struct objc_class *isa;
/* extra stuff */
};
the *isa points to a class.. some class. So the object you're setting as a delegate might just not exist or point to bad memory.