When are C++ objects destroyed in Objective-C++? - objective-c

I am trying to expose a C++ object by creating an Objective-C++ class to wrap it.
Ultimately, in Swift, I'm trying to write this:
print(JSApplication.eval("'TKTK'")?.toString() ?? "")
print(JSApplication.eval("x = {a: 42}; x")?.toString() ?? "")
print(JSApplication.eval("x")?.get("a")?.toString() ?? "nope");
print(JSApplication.eval("1 + 2")?.toInt32() ?? 0)
However, when I try to call JSApplication.eval("x")?.get("a")?.toString(), Objective-C calls dealloc on my class after .get("a") but before .toString().
Normally in dealloc I would call .reset() on the shared pointer that the class contains. But since the dealloc is firing too early, this would clear out my V8 result before .toString() could be called on it.
This raises a general question: How does Swift / Objective-C decide when to call dealloc on a temporary object? For something like foo()?.bar()?.baz(), where foo and bar return temporary objects, is it correct that both temp objects are receiving a dealloc message before baz is called? That's what I'm seeing.
If that's the correct behavior, then what's the proper way to extend the lifetime of a temporary object to the scope where the function is being called, like C++? Is that possible?
Here's my Objective-C++ binding. (I notice that no "Destroy 0x..." messages are being printed out at all, so C++ destructors don't seem to be firing. Am I supposed to call those manually?)
// Extracts a C string from a V8 Utf8Value.
const char* ToCString(const v8::String::Utf8Value& value) {
return *value ? *value : "<string conversion failed>";
}
#interface NJSValue (V8)
- (instancetype)init;
- (instancetype)initWithValue:(Local<Value>)value;
#end
struct NJSRef
{
std::shared_ptr<Nan::Persistent<Value>> _ref;
~NJSRef()
{
printf("Destroy 0x%08x\n", (unsigned int)(size_t)_ref.get());
}
};
#implementation NJSValue (V8)
NJSRef m;
- (instancetype)init
{
self = [super init];
return self;
}
- (instancetype)initWithValue:(Local<Value>)value
{
self = [super init];
Nan::HandleScope scope;
Nan::EscapableHandleScope escape;
m._ref.reset(new Nan::Persistent<Value>(escape.Escape(value)));
printf("Alloc 0x%08x\n", (unsigned int)(size_t)m._ref.get());
return self;
}
#end
#implementation NJSValue
- (void)dealloc
{
printf("Dealloc 0x%08x\n", (unsigned int)(size_t)m._ref.get());
//m_ref.reset();
}
- ( NSString * _Nonnull )toString
{
if (m._ref != nullptr) {
Nan::HandleScope scope;
Local<Value> value(Nan::New(*m._ref));
v8::String::Utf8Value str(JS_ISOLATE(), value);
const char* cstr = ToCString(str);
return NJSStringToNSString(JS_STR(cstr));
} else {
return #"undefined";
}
}
- (NSNumber *)toInt32
{
if (m._ref != nullptr) {
Nan::HandleScope scope;
Local<Value> value(Nan::New(*m._ref));
if (!value->IsInt32()) return nullptr;
return [NSNumber numberWithInt:TO_INT32(value)];
} else {
return nullptr;
}
}
- (NSNumber *)toNumber
{
if (m._ref != nullptr) {
Nan::HandleScope scope;
Local<Value> value(Nan::New(*m._ref));
if (!value->IsNumber()) return nullptr;
return [NSNumber numberWithDouble:TO_DOUBLE(value)];
} else {
return nullptr;
}
}
- (NJSValue * _Nullable __strong)get:(NSString * _Nonnull)key CF_RETURNS_RETAINED
{
if (m._ref == nullptr) return nullptr;
v8::HandleScope scope(JS_ISOLATE());
v8::EscapableHandleScope handle_scope(JS_ISOLATE());
Local<Value> value(Nan::New(*m._ref));
if (!value->IsObject()) return nullptr;
Local<Object> obj(JS_OBJ(value));
Local<Value> jsKey(JS_STR([key UTF8String]));
if (!obj->Has(JS_CONTEXT(), jsKey).FromJust()) return nullptr;
Local<Value> result(obj->Get(jsKey));
v8::String::Utf8Value str(JS_ISOLATE(), result);
const char* cstr = ToCString(str);
printf("got %s\n", cstr);
NJSValue* ret = [[NJSValue alloc] initWithValue:handle_scope.Escape(result)];
// [self associateValue:ret withKey:key];
return ret;
}
#end
#implementation JSApplication
- (instancetype)init
{
self = [super init];
if (self) {
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super init];
if (self) {
self.frame = frame;
}
return self;
}
// Executes a string within the current v8 context.
v8::Local<v8::Value>
ExecuteString(v8::Isolate* isolate, v8::Local<v8::String> source,
v8::Local<v8::Value> name, bool print_result,
bool report_exceptions) {
v8::EscapableHandleScope handle_scope(isolate);
v8::TryCatch try_catch(isolate);
v8::ScriptOrigin origin(name);
v8::Local<v8::Context> context(isolate->GetCurrentContext());
v8::Local<v8::Script> script;
if (!v8::Script::Compile(context, source, &origin).ToLocal(&script)) {
// Print errors that happened during compilation.
if (report_exceptions)
ReportException(isolate, &try_catch);
return handle_scope.Escape(v8::Undefined(isolate));
} else {
v8::Local<v8::Value> result;
if (!script->Run(context).ToLocal(&result)) {
assert(try_catch.HasCaught());
// Print errors that happened during execution.
if (report_exceptions)
ReportException(isolate, &try_catch);
return handle_scope.Escape(v8::Undefined(isolate));
} else {
assert(!try_catch.HasCaught());
if (print_result && !result->IsUndefined()) {
// If all went well and the result wasn't undefined then print
// the returned value.
v8::String::Utf8Value str(isolate, result);
const char* cstr = ToCString(str);
printf("eval result: %s\n", cstr);
}
return handle_scope.Escape(result);
}
}
}
void ReportException(v8::Isolate* isolate, v8::TryCatch* try_catch) {
v8::HandleScope handle_scope(isolate);
v8::String::Utf8Value exception(isolate, try_catch->Exception());
const char* exception_string = ToCString(exception);
v8::Local<v8::Message> message = try_catch->Message();
if (message.IsEmpty()) {
// V8 didn't provide any extra information about this error; just
// print the exception.
fprintf(stderr, "%s\n", exception_string);
} else {
// Print (filename):(line number): (message).
v8::String::Utf8Value filename(isolate,
message->GetScriptOrigin().ResourceName());
v8::Local<v8::Context> context(isolate->GetCurrentContext());
const char* filename_string = ToCString(filename);
int linenum = message->GetLineNumber(context).FromJust();
fprintf(stderr, "%s:%i: %s\n", filename_string, linenum, exception_string);
// Print line of source code.
v8::String::Utf8Value sourceline(
isolate, message->GetSourceLine(context).ToLocalChecked());
const char* sourceline_string = ToCString(sourceline);
fprintf(stderr, "%s\n", sourceline_string);
// Print wavy underline (GetUnderline is deprecated).
int start = message->GetStartColumn(context).FromJust();
for (int i = 0; i < start; i++) {
fprintf(stderr, " ");
}
int end = message->GetEndColumn(context).FromJust();
for (int i = start; i < end; i++) {
fprintf(stderr, "^");
}
fprintf(stderr, "\n");
v8::Local<v8::Value> stack_trace_string;
if (try_catch->StackTrace(context).ToLocal(&stack_trace_string) &&
stack_trace_string->IsString() &&
v8::Local<v8::String>::Cast(stack_trace_string)->Length() > 0) {
v8::String::Utf8Value stack_trace(isolate, stack_trace_string);
const char* stack_trace_string = ToCString(stack_trace);
fprintf(stderr, "%s\n", stack_trace_string);
}
}
}
+ (NJSValue *)Eval:(NSString *)string __attribute((ns_returns_retained))
{
Isolate* isolate = Isolate::GetCurrent();
v8::HandleScope handle_scope(isolate);
Local<Context> context = isolate->GetCurrentContext();
v8::Context::Scope context_scope(context);
const char* str = [string UTF8String];
Local<Value> result = ExecuteString(
context->GetIsolate(),
v8::String::NewFromUtf8(context->GetIsolate(), str,
v8::NewStringType::kNormal).ToLocalChecked(),
JS_STR("JSApplication.Eval"), false, true);
return [[NJSValue alloc] initWithValue:result];
}
#end
Here's the output I get for the Swift code at the top of this question:
TKTK
Alloc 0x81d34230
Dealloc 0x81d34230
Alloc 0x81d34270
Dealloc 0x81d34270
TKTK
Alloc 0x81d38230
Dealloc 0x81d38230
[object Object]
Alloc 0x81d38250
got 42
Alloc 0x81d38240
Dealloc 0x81d38240
Dealloc 0x81d38240
42
Alloc 0x81d38250
Dealloc 0x81d38250
3

A big part of your problem is that your declaration of NJSRef m; is not declaring an instance variable, even though it's inside an #implementation. It's just a file-scope global. There's just one, and it's being shared (and clobbered) by all of your instances of NJSValue. You would have to enclose it in curly braces {...} to make it an instance variable.
That explains why it's never destroyed, at least. Probably a lot of the other symptoms, too, but it's hard to tell given the external types you're using that I'm not familiar with.

Related

Recursive function with block in Objective C

I've got a function with completion block, and which in some cases calls itself recursively until has the expected result.
I'm getting errors in release, probably because it has different memory management in release.
Anyway I'm asking what should I do to make it work in release as in debug.
I've found some links about declaring a block variable weak, and another one strong, and then assign the block to both of them. However I'm in difficulty to understand well how to apply it to my code.
Here's the code:
-(void) getBitmapFromXObject: (OCPdfXObject *) xobj completion:(void(^)(NSData * data))completion{
__block BOOL hasAlreadyCompleted = false;
if (xobj.type == OCPdfXObjectType_Form) {
OCPdfXObjectForm * formxboj = (OCPdfXObjectForm *)xobj;
OCPdfDictionary * res = formxboj.resources.xObject;
if (res != nil) {
for (CBKeyValuePair * key in res) {
OCPdfXObject* childXObjc = [OCPdfXObject createFromObject:key.value];
if (!childXObjc) {
continue;
}
[self getBitmapFromXObject:childXObjc completion:^(NSData *data) {
if (data != nil) {
hasAlreadyCompleted = true;
}
}];
}
}
}
if (xobj.type == OCPdfXObjectType_Image) {
OCPdfImage* imagexobj = (OCPdfImage *)xobj;
if (imagexobj.colorSpace != OCPdfColorSpace_DeviceRGB) {
completion(nil);
hasAlreadyCompleted = true;
}
[self getBitmapFromImage:imagexobj withCompletion:^(NSData *data) {
NSData *uiImage = data;
completion(uiImage);
hasAlreadyCompleted = true;
}];
}
if (!hasAlreadyCompleted) {
completion(nil);
}
}

What do non-ARC Objective-C property accessors look like?

I want to know how the getter and setter for an Objective-C property are implemented as part of learning the concept of memory management.
I have not been able to find an actual representation other than "nonatomic" and "atomic".
What does the actual code look like for getters and setters with the different property attributes, such as strong/weak, copy/assign, and __unsafe_unretained?
You can check the source code at the objc4 github repo
getter: https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-accessors.mm#L48
id objc_getProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
if (offset == 0) {
return object_getClass(self);
}
// Retain release world
id *slot = (id*) ((char*)self + offset);
if (!atomic) return *slot;
// Atomic retain release world
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
id value = objc_retain(*slot);
slotlock.unlock();
// for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
return objc_autoreleaseReturnValue(value);
}
setter: https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-accessors.mm#L70
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
if (offset == 0) {
object_setClass(self, newValue);
return;
}
id oldValue;
id *slot = (id*) ((char*)self + offset);
if (copy) {
newValue = [newValue copyWithZone:nil];
} else if (mutableCopy) {
newValue = [newValue mutableCopyWithZone:nil];
} else {
if (*slot == newValue) return;
newValue = objc_retain(newValue);
}
if (!atomic) {
oldValue = *slot;
*slot = newValue;
} else {
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
objc_release(oldValue);
}
load weak variable: https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/NSObject.mm#L444
id
objc_loadWeakRetained(id *location)
{
id result;
SideTable *table;
retry:
result = *location;
if (!result) return nil;
table = &SideTables()[result];
table->lock();
if (*location != result) {
table->unlock();
goto retry;
}
result = weak_read_no_lock(&table->weak_table, location);
table->unlock();
return result;
}
I don't think there is any code for unsafe_retained. Compiler can just simply assign the pointer without anything else.

How do I find the minimum value of a binary tree?

I'm trying to find the minimum value of a binary tree. Every time I run my code I get a long 5 digit number like '32675'. I'm pretty sure my understanding of pointers is wrong, but I'm not positive. If I could get some advice I'd really appreciate it. Thanks!
Node definition
#interface Node:NSObject {
#property (nonatomic, strong) Node *left;
#property (nonatomic, strong) Node *right;
#property (nonatomic, assign) int *value;
}
-(id)initWithValue:(int)val {
self = [super init];
if(self) {
self.value = &(val);
self.left = nil;
self.right = nil;
}
return self;
}
Insert Algorithms for Tree
-(void)insertValue:(int)value {
Node *node = [[Node alloc] initWithValue:value];
[self insertNode:node];
}
-(void)insertNode:(Node *)node {
if (root == nil) {
root = node;
} else {
[node insertNode:node];
}
}
Insert Algorithms for Node
-(void)insertNode:(Node *)node{
if (node.value < self.value) {
[self insertOnLeft:node];
} else {
[self insertOnRight:node];
}
}
-(void)insertOnLeft:(Node *)node {
if (self.left == nil) {
self.left = node;
} else {
[self.left insertNode:node];
}
}
-(void)insertOnRight:(Node *)node {
if (self.right == nil) {
self.right = node;
} else {
[self.right insertNode:node];
}
}
3 values go in to my tree:
[tree insertValue:4];
[tree insertValue:6];
[tree insertValue:2];
int min = [tree findMinimum];
Tree's findMinimum method is called
-(int)findMinimum {
assert(root != nil);
return [root findMinimum];
}
Which call's root's findMinimum - root is a node
-(int)findMinimum {
Node *node = self;
int min = 0;
while (node != nil) {
min = *(node.value);
node = node.left;
}
return min;
}
Your are filling your tree not with integers but with stack addresses which are immediately invalid with this code:
self.value = &(val);
Here val is a parameter variable, which will disappear as soon as the method returns. Taking its address should only be done in rare circumstances and that address should never be stored in a location which outlives val.
Change the type of the value property of Node to int and remove the uses of address of (&) and indirection (*) associated with that property.
HTH
The assignment in the initializer of a pointer to the parameter won't work. Unless you have a good reason not to, actually allocate the int into the Node structure by declaring it an int, not an int *. So your initializer will look like this:
-(id)initWithValue:(int)val {
self = [super init];
if(self) {
self.value = val;
}
return self;
}
EDIT If you weren't sorting on insert (which I missed in the OP), you could instead search for the min recursively as follows:
-(int)findMinimum {
if (self.left && self.right)
return MIN([self.left findMinimum], [self.right findMinimum]);
else if (self.left)
return [self.left findMinimum];
else if (self.right)
return [self.right findMinimum];
else
return self.value;
}

Checking and Remedying Null Objects Efficiently

Has anyone found a more efficient way of checking if a variable is nil, then instantiating it? I know the classic way is to write:
if (myObject == nil) {
//instantiate and do other stuff
}
Or
if (!myobect) {
//do stuff
}
Which seems too boiler-plate-esque for my liking, especially if I have to do it many times.
EDIT: Having had a stroke of inspiration, would something like this work?
-(id)instantiateIfObjectIsNull:(id)object withClass:(Class*)class {
if (object == nil) {
object = [[Class alloc]init];
}
return object;
}
If you're using ObjC++, you could approach it from this angle:
#import <Foundation/Foundation.h>
template < typename T >
void InitIfNil(T*& obj) {
if (nil == obj)
obj = [T new];
}
template < typename T >
void TryIt() {
T * obj(nil);
NSLog(#"before: %#", obj);
InitIfNil(obj);
NSLog(#"after: %s : %#", object_getClassName(obj), obj);
[obj release];
}
int main(int argc, const char* argv[]) {
#autoreleasepool {
TryIt<NSArray>();
TryIt<NSString>();
TryIt<NSMutableSet>();
}
return 0;
}
This could also be done with a macro... I don't think this helps readability though.
A third option would be C11 generics - when your compiler supports them.
well it is essentially the same thing but
if (!myobject) {
.....
}

List selectors for Objective-C object

I have an object, and I want to list all the selectors to which it responds. It feels like this should be perfectly possible, but I'm having trouble finding the APIs.
This is a solution based on the runtime C functions:
class_copyMethodList returns a list of class methods given a Class object obtainable from an object.
#import <objc/runtime.h>
[..]
SomeClass * t = [[SomeClass alloc] init];
int i=0;
unsigned int mc = 0;
Method * mlist = class_copyMethodList(object_getClass(t), &mc);
NSLog(#"%d methods", mc);
for(i=0;i<mc;i++)
NSLog(#"Method no #%d: %s", i, sel_getName(method_getName(mlist[i])));
/* note mlist needs to be freed */
I think usually you'll want to do that in the console, instead of cluttering your code with debug code. This is how you can do that while debugging in lldb:
(Assuming an object t)
p int $num = 0;
expr Method *$m = (Method *)class_copyMethodList((Class)object_getClass(t), &$num);
expr for(int i=0;i<$num;i++) { (void)NSLog(#"%s",(char *)sel_getName((SEL)method_getName($m[i]))); }
This is also possible with Swift:
let obj = NSObject()
var mc: UInt32 = 0
let mcPointer = withUnsafeMutablePointer(&mc, { $0 })
let mlist = class_copyMethodList(object_getClass(obj), mcPointer)
print("\(mc) methods")
for i in 0...Int(mc) {
print(String(format: "Method #%d: %s", arguments: [i, sel_getName(method_getName(mlist[i]))]))
}
Output:
251 methods
Method #0: hashValue
Method #1: postNotificationWithDescription:
Method #2: okToNotifyFromThisThread
Method #3: fromNotifySafeThreadPerformSelector:withObject:
Method #4: allowSafePerformSelector
Method #5: disallowSafePerformSelector
...
Method #247: isProxy
Method #248: isMemberOfClass:
Method #249: superclass
Method #250: isFault
Method #251: <null selector>
Tested with the 6s simulator running iOS 9.2, Xcode Version 7.2 (7C68).
Taking inspiration from JAL's answer, in Swift you can do:
extension NSObject {
var __methods: [Selector] {
var methodCount: UInt32 = 0
guard
let methodList = class_copyMethodList(type(of: self), &methodCount),
methodCount != 0
else { return [] }
return (0 ..< Int(methodCount))
.flatMap({ method_getName(methodList[$0]) })
}
}
ARC realization
SomeClass *someClass = [[SomeClass alloc] init];
//List of all methods
unsigned int amountMethod = 0;
Method *methods = class_copyMethodList(someClass, &amountMethod);
for (unsigned int i = 0; i < amountMethod; i++) {
Method method = methods[i];
printf("\t method named:'%s' \n", sel_getName(method_getName(method)));
}
free(methods);
Something like this should work (just put it in the object you're curious about). For example if you have an object that's a delegate and want to know what 'hooks' are available this will print out messages to give you that clue:
-(BOOL) respondsToSelector:(SEL)aSelector {
printf("Selector: %s\n", [NSStringFromSelector(aSelector) UTF8String]);
return [super respondsToSelector:aSelector];
}
Note that I discovered this in the iPhone Developer's Cookbook so I can't take credit! For example output I get from a UIViewController that implements the protocols <UITableViewDelegate, UITableViewDataSource>:
Selector: tableView:numberOfRowsInSection:
Selector: tableView:cellForRowAtIndexPath:
Selector: numberOfSectionsInTableView:
Selector: tableView:titleForHeaderInSection:
Selector: tableView:titleForFooterInSection:
Selector: tableView:commitEditingStyle:forRowAtIndexPath:
Selector: sectionIndexTitlesForTableView:
Selector: tableView:sectionForSectionIndexTitle:atIndex:
...
...
etc.,etc.
If you want to also get the selectors for the super classes you can loop through like this:
Class c = [myObject class];
while (c != nil) {
int i = 0;
unsigned int mc = 0;
Method* mlist = class_copyMethodList(c, &mc);
NSLog(#"%d methods for %#", mc, c);
for(i = 0; i < mc; i++) {
const char* selName = sel_getName(method_getName(mlist[i]));
NSLog(#"Method #%d: %s", i, selName);
}
free(mlist);
c = [c superclass];
}