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);
}
}
Related
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.
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.
I do not know how to convert the following Objective-C code into swift. How should I do ?
Objective-c
if (operations) {
if ([operations isKindOfClass:[NSArray class]]) {
for (id <MyOperation> operation in operations) {
if (operation) {
[operation cancel];
}
}
} else if ([operations conformsToProtocol:#protocol(MyOperation)]){
[(id<MyOperation>) operations cancel];
}
[operationDictionary removeObjectForKey:key];
}
swift
if operations != nil {
// doto .......
}
Try this code:
if operations != nil {
// doto .......
if (operations?.isKindOfClass(NSArray) != nil){
for operation in operations as! [MyOperation]{
operation.cancle()
}
} else if operations?.conformsToProtocol(MyOperation){
(operations as MyOperation).cancle()
}
operationDictionary.removeObjectForKey(key)
}
I didn't test it so may be you have to make little bit changes into this code.
Hope this will help.
I usually don't help people with complete conversions, but...
if let operations: AnyObject = operations {
if operations is NSArray {
for operation in operations as! [MyOperation] {
operation.cancel()
}
}
else if let operation = operations as? MyOperation {
operation.cancel()
}
operationDictionary.removeObjectForKey(key)
}
I have these declarations in header file:
Note: I won't explain the whole code, I think it is easy to understand
typedef void (^loopCell)(id cell);
-(id)allCells:(loopCell)cell;
And allCells function implementation:
-(id)allCells:(loopCell)cell
{
for (AAFormSection *section in listSections)
{
for (id _cell in section.fields) {
cell(_cell);
}
}
return nil;
}
The usage of allCells function:
-(void)setFieldValue:(NSString *)value withID:(int)rowID
{
[self allCells:^(id cell) {
if([cell isKindOfClass:[AAFormField class]]) {
AAFormField *_cell = (AAFormField *)cell;
if(_cell.rowID == rowID) {
_cell.value = value;
//return; Here I want to terminate loop
}
}
}];
}
My problem is, I can't terminate allCells loop in the middle (actually when I found object I need in the loop, I don't want iterate through other objects)
How can I stop allCells loop in the middle?
Look at the docs for NSArray enumerateObjectsUsingBlock:. They setup the block signature to take a BOOL pointer. Set the stop BOOL to YES to cause the iteration to stop.
typedef void (^loopCell)(id cell, BOOL *stop);
-(id)allCells:(loopCell)cell {
BOOL stop = NO;
for (AAFormSection *section in listSections) {
for (id _cell in section.fields) {
cell(_cell, &stop);
if (stop) {
break;
}
}
if (stop) {
break;
}
}
return nil;
}
-(void)setFieldValue:(NSString *)value withID:(int)rowID {
[self allCells:^(id cell, BOOL *stop) {
if([cell isKindOfClass:[AAFormField class]]) {
AAFormField *_cell = (AAFormField *)cell;
if(_cell.rowID == rowID) {
_cell.value = value;
if (stop) {
*stop = YES;
}
}
}
}];
}
You can't break from setFieldValue, but you can from allCells.
It's up to the method you're using that calls the block — allCells in this case — to provide a mechanism for stopping the loop. Usually, it's a parameter to the block.
If allCells is yours and you don't mind modifying it, you modify the block signature to take a pointer to a BOOL, initialized to YES, and check if the block modified it to NO.
(Note: You can break from a for in loop.)
thats my first question here and i hope someone can help me.
I´m new at the iPhone programming and want to try an easy app...
It´s an SudokuSolver which is working with an recursive Method. In JAVA this code is making no problems, but in Objective-C the code isn´t stopping when Sudoku is solved. It´s still trying to solve the Sudoku and stops later.
Anyone an idea?!
Here´s the code.
- (SudokuSolver *) initWithField: (int[9][9]) field {
self = [super init];
if(self) {
for (int i=0; i<9; i++) {
for (int j=0; j<9; j++) {
sudokuField[i][j] = field[i][j];
if (field[i][j]) {
sudokuFieldStatic[i][j] = 1;
} else {
sudokuFieldStatic[i][j] = 0;
}
}
}
}
return self;
}
- (void) solve {
[self solveFieldAtRow:0 andCol:0];
}
- (void) solveFieldAtRow: (int) row andCol: (int) col {
if (row > 8) {
return;
} else {
while (sudokuField[row][col] != 0) {
if (++col > 8) {
col = 0;
row++;
if (row > 8) {
return;
}
}
}
for (int num=1; num<10; num++) {
if ([self checkRow:row forNumber:num] && [self checkCol:col forNumber:num] && [self checkFieldAtRow:row andCol:col forNumber:num]) {
sudokuField[row][col] = num;
[self showFieldInConsole:0];
if (col < 8) {
[self solveFieldAtRow:row andCol:col+1];
} else {
[self solveFieldAtRow:row+1 andCol:0];
}
}
}
sudokuField[row][col] = 0;
}
}
The code isn't stopping when the puzzle is solved because you don't check whether the puzzle is solved after the recursive call. So even if the recursive call found a solution, the code just continues on even after finding a solution until it has tried every possibility.
Since you say you have Java code that works, I suggest you compare the logic of the Java program versus this code. You'll probably find the Java code does include such a test.
Edit From your comment above, I see that you won't find such a test in your Java code, because there you are abusing exceptions to "return" from the recursion when a solution is found. The proper way is to have each recursive call return a true value if it found a solution and false if it didn't. And then each step should check if its child call succeeded, and itself return success if so. Something like this:
- (BOOL) solveFieldAtRow: (int) row andCol: (int) col {
if (row > 8) {
// reached the end, so it must have succeeded
return YES;
} else {
while (sudokuField[row][col] != 0) {
if (++col > 8) {
col = 0;
row++;
if (row > 8) {
// reached the end, so it must have succeeded
return YES;
}
}
}
for (int num=1; num<10; num++) {
if ([self checkRow:row forNumber:num] && [self checkCol:col forNumber:num] && [self checkFieldAtRow:row andCol:col forNumber:num]) {
sudokuField[row][col] = num;
[self showFieldInConsole:0];
BOOL result;
if (col < 8) {
result = [self solveFieldAtRow:row andCol:col+1];
} else {
result = [self solveFieldAtRow:row+1 andCol:0];
}
if (result) {
// Our child call succeeded, so we pass that back up
// the stack.
return YES;
}
}
}
sudokuField[row][col] = 0;
// If we get here, we could not find a solution. Return failure
// back up the stack.
return NO;
}
}