I am writing a C++ program that has one class that has an Objective-C++ implementation. Each function has an autorelease pool when there is any opportunity for object creation.
When a specific object function gets called more than once I get 3 log messages of the type *** __NSAutoreleaseNoPool(): Object 0x10b730 of class NSCFArray autoreleased with no pool in place - just leaking
There is an autorelease pool in this specific function and also in either of the 2 functions that can call it. Is it possible that one of the frameworks I am using is creating some global objects that get leaked ?
I tried setting a breakpoint on __NSAutoreleaseNoPool but it won't break. I also set NSAutoreleaseHaltOnNoPool and couldn't break either.
EDIT: Here is the code,
qtdata.h :
#ifndef qtdata_h
#define qtdata_h
#include "../videodata.h"
class QTData : public VideoData
{
public:
QTData();
~QTData() { };
bool Open(const char *seqname, QImage *img = NULL);
bool ReadFirstFrame(const char *seqname, QImage &img);
private:
bool getFrame(void *handle, QImage *img);
};
#endif
qtdata.mm :
#include <CoreAudio/CoreAudio.h>
#include <QuickTime/Movies.h>
#import <QTKit/QTKit.h>
#include "qtdata.h"
QTData::QTData() : VideoData(){ };
// Open and read first frame into img
bool QTData::Open(const char *seqname, QImage *img)
{
NSAutoreleasePool *localpool = [[NSAutoreleasePool alloc] init];
NSError *error;
QTMovie *movieHandle;
movieHandle = [[QTMovie movieWithFile:[NSString stringWithCString:seqname
encoding:NSUTF8StringEncoding] error:&error] retain];
if(movieHandle == nil)
{
[localpool release];
return(false);
}
[movieHandle gotoBeginning];
NSSize size = [[movieHandle attributeForKey:QTMovieNaturalSizeAttribute] sizeValue];
width = size.width;
height = size.height;
bool success = false;
if(img)
{
[movieHandle gotoBeginning];
success = getFrame(movieHandle, img);
}
[localpool drain];
return(success);
}
bool QTData::getFrame(void *handle, QImage *img)
{
bool success = false;
NSAutoreleasePool *localpool = [[NSAutoreleasePool alloc] init];
NSDictionary *attributes = [NSDictionary dictionaryWithObject:QTMovieFrameImageTypeCVPixelBufferRef
forKey:QTMovieFrameImageType];
CVPixelBufferRef frame = (CVPixelBufferRef)[(QTMovie *)handle frameImageAtTime:[(QTMovie *)handle currentTime]
withAttributes:attributes error:nil];
CVPixelBufferLockBaseAddress(frame, 0);
QImage *buf;
int r, g, b;
char *pdata = (char *)CVPixelBufferGetBaseAddress(frame);
int stride = CVPixelBufferGetBytesPerRow(frame);
buf = new QImage(width, height, QImage::Format_RGB32);
for(int j = 0; j < height; j++)
{
for(int i = 0; i < width; i++)
{
r = *(pdata+(i*4)+1);
g = *(pdata+(i*4)+2);
b = *(pdata+(i*4)+3);
buf->setPixel(i, j, qRgb(r, g, b));
}
pdata += stride;
}
success = true;
*img = *buf;
delete buf;
CVPixelBufferUnlockBaseAddress(frame, 0);
CVBufferRelease(frame);
[localpool drain];
return(success);
}
bool QTData::ReadFirstFrame(const char *seqname, QImage &img)
{
NSAutoreleasePool *localpool = [[NSAutoreleasePool alloc] init];
NSError *error = nil;
QTMovie *movieHandle;
movieHandle = [[QTMovie movieWithFile:[NSString stringWithCString:seqname
encoding:NSUTF8StringEncoding] error:&error] retain];
if(movieHandle == nil)
{
[localpool drain];
return(false);
}
[movieHandle gotoBeginning];
bool success = getFrame(movieHandle, &img);
[localpool drain];
return(success);
}
You have to create autorelease pool for each new thread. check main.m, it create it for the main thread.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// your code
[pool drain];
read NSAutoreleasePool doc
Note: If you are creating secondary threads using the POSIX thread APIs
instead of NSThread objects, you cannot use Cocoa, including
NSAutoreleasePool, unless Cocoa is in multithreading mode. Cocoa
enters multithreading mode only after detaching its first NSThread
object. To use Cocoa on secondary POSIX threads, your application must
first detach at least one NSThread object, which can immediately exit.
You can test whether Cocoa is in multithreading mode with the NSThread
class method isMultiThreaded.
update:
So you have to create a NSThread object to enable Cocoa multithreading mode.
NSThread *thread = [[NSThread alloc] init];
[thread start];
[thread release];
Than wrap all of the obj-c code in a autorelease pool like code above.
Related
How can I pass a class as argument to a thread using NSThread. In windows I was doing something like:
DWORD WINAPI threadFunc(LPVOID mpThis) {
MYCLSS *pThis = reinterpret_cast<MYCLSS*>(mpThis);
....
void MYCLSS::func() {
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadFunc, (void*)this, 0, NULL);
....
For Mac I have not found any examples of this kind. Please help me
I found the solution, it can be like windows
class CLS_CPP {
public:
void Func();
void Check();
int var_check;
};
#interface CLS_OSX : NSObject {
#public
void *obj;
}
-(void)osx_thread;
#end
#implementation CLS_OSX
-(void)osx_thread {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
CLS_CPP *pThis = reinterpret_cast<CLS_CPP*>(obj);
pThis->Check();
NSLog(#"Check var %d", pThis->var_check);
[NSThread exit];
[pool drain];
}
#end
void CLS_CPP::Func() {
var_check=77;
CLS_OSX *trgt=[[CLS_OSX alloc] init];
trgt->obj=(void*)this;
[NSThread detachNewThreadSelector:#selector(osx_thread) toTarget:trgt withObject:trgt];
}
void CLS_CPP::Check() {
NSLog(#"CLS_CPP::Check success");
}
int main(int argc, char ** argv) {
CLS_CPP oAPP;
oAPP.Func();
}
Isn't taking a screenshot on Windows thread-safe?
My following code sometimes takes some shots, but in most cases, the imgScreenshot (which is just a TImage) keeps being just plain white...
Am I missing something?
void __fastcall TCaptureThread::Execute()
{
int CurWidth = 1600;
int CurHeight = 900;
std::unique_ptr<TCanvas> Canvas(new TCanvas);
Canvas->Handle = GetDC(0);
FBMP = new TBitmap; // private class field
FBMP->Width = CurWidth;
FBMP->Height = CurHeight;
FR = Rect(0, 0, CurWidth, CurHeight); // private class field
while(!Terminated)
{
FBMP->Canvas->CopyRect(FR, Canvas, FR);
Synchronize(&UpdatePicture);
Sleep(100);
}
delete FBMP;
FBMP = NULL;
}
void __fastcall TCaptureThread::UpdatePicture()
{
FMainForm->imgScreenshot->Canvas->CopyRect(FR, FBMP->Canvas, FR);
}
Environment is C++ Builder 10.1.2 Berlin
Isn't taking a screenshot on Windows thread-safe?
Not when using VCL wrapper classes that are not thread-safe by default. If you were using plain Win32 API functions directly, then yes, it would be possible to write thread-safe code.
The main reason your code fails is because the VCL is designed to share GDI resources between multiple objects, and the main UI thread frequently frees unused/dormant GDI resources. So your worker thread's TBitmap image data is likely to get destroyed before you can call Synchronize() to copy it to your TImage.
That being said, what you are attempting can be done if you call Lock()/Unlock() on the Canvas objects in your worker thread, eg:
struct CanvasLocker
{
TCanvas *mCanvas;
CanvasLocker(TCanvas *C) : mCanvas(C) { mCanvas->Lock(); }
~CanvasLocker() { mCanvas->Unlock(); }
};
void __fastcall TCaptureThread::Execute()
{
int CurWidth = 1600;
int CurHeight = 900;
std::unique_ptr<TCanvas> Canvas(new TCanvas);
std::unique_ptr<TBitmap> BMP(new TBitmap);
FBMP = BMP.get();
{
CanvasLocker lock(Canvas); // <-- add this!
Canvas->Handle = GetDC(0);
}
{
CanvasLocker lock(BMP->Canvas); // <-- add this!
BMP->Width = CurWidth;
BMP->Height = CurHeight;
}
FR = Rect(0, 0, CurWidth, CurHeight);
while (!Terminated)
{
{
CanvasLocker lock1(Canvas); // <-- add this!
CanvasLocker lock2(BMP->Canvas); // <-- add this!
BMP->Canvas->CopyRect(FR, Canvas.get(), FR);
}
Synchronize(&UpdatePicture);
Sleep(100);
}
}
void __fastcall TCaptureThread::UpdatePicture()
{
CanvasLocker lock1(FBMP->Canvas); // <-- add this!
CanvasLocker lock2(FMainForm->imgScreenshot->Canvas); // <-- add this!
FMainForm->imgScreenshot->Canvas->CopyRect(FR, FBMP->Canvas, FR);
// or: FMainForm->imgScreenshot->Picture->Bitmap->Assign(FBMP);
}
That being said, because TCanvas is lockable, you might be able to get away with removing Synchronize() altogether:
void __fastcall TCaptureThread::Execute()
{
int CurWidth = 1600;
int CurHeight = 900;
std::unique_ptr<TCanvas> Canvas(new TCanvas);
std::unique_ptr<TBitmap> BMP(new TBitmap);
{
CanvasLocker lock(Canvas);
Canvas->Handle = GetDC(0);
}
{
CanvasLocker lock(BMP->Canvas);
BMP->Width = CurWidth;
BMP->Height = CurHeight;
}
TRect r = Rect(0, 0, CurWidth, CurHeight);
while (!Terminated)
{
{
CanvasLocker lock1(BMP->Canvas);
{
CanvasLocker lock2(Canvas);
BMP->Canvas->CopyRect(r, Canvas.get(), r);
}
CanvasLocker lock3(FMainForm->imgScreenshot->Canvas);
FMainForm->imgScreenshot->Canvas->CopyRect(r, BMP->Canvas, r);
// or: FMainForm->imgScreenshot->Picture->Bitmap->Assign(BMP);
}
Sleep(100);
}
}
i have 2 void functions(trying to implement radio button), i want them to send value to a third function by swapping values. and that function returning value to main function?
CODE OF MY MyScene.h FILE
#ifndef __MY_SCENE_H__
#define __MY_SCENE_H__
#include "cocos2d.h"
USING_NS_CC;
class MyScene : public cocos2d::CCLayerColor
{
public:
// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init();
// there's no 'id' in cpp, so we recommand to return the exactly class pointer
static cocos2d::CCScene* scene();
CCMenuItemToggle *R;
CCMenuItemToggle *L;
// a selector callback
void swapL(CCObject *sender);
void swapR(CCObject *sender);
// implement the "static node()" method manually
LAYER_NODE_FUNC(MyScene);
};
#endif // __HELLOWORLD_SCENE_H__
CODE OF MY MyScene.cpp FILE
#include "MyScene.h"
USING_NS_CC;
CCScene* MyScene::scene()
{
// 'scene' is an autorelease object
CCScene *scene = CCScene::node();
// 'layer' is an autorelease object
MyScene *layer = MyScene::node();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool MyScene::init()
{
//////////////////////////////
// 1. super init first
if ( !CCLayerColor::initWithColor(ccc4(255,255,255,255) ))
{
return false;
}
//////////////////////////////
// 2. add your codes below...
CCSize WinSize= CCDirector::sharedDirector()->getWinSizeInPixels();
CCSprite * fish=CCSprite::spriteWithFile("fish_bg.png");
fish->setPosition(CCPointZero);
fish->setAnchorPoint(CCPointZero);
fish->setScaleX(WinSize.width/480);
fish->setScaleY(WinSize.height/395);
this->addChild(fish,0,0);
CCSprite * on1=CCSprite::spriteWithFile("on.png");
CCSprite * on2=CCSprite::spriteWithFile("on.png");
CCSprite * on3=CCSprite::spriteWithFile("on.png");
CCSprite * on4=CCSprite::spriteWithFile("on.png");
CCSprite * off1=CCSprite::spriteWithFile("off.png");
CCSprite * off2=CCSprite::spriteWithFile("off.png");
CCSprite * off3=CCSprite::spriteWithFile("off.png");
CCSprite * off4=CCSprite::spriteWithFile("off.png");
CCMenuItem *LeftOn=CCMenuItemSprite::itemFromNormalSprite(on1,on2);
CCMenuItem *RightOn=CCMenuItemSprite::itemFromNormalSprite(on3,on4);
CCMenuItem *LeftOff=CCMenuItemSprite::itemFromNormalSprite(off1,off2);
CCMenuItem *RightOff=CCMenuItemSprite::itemFromNormalSprite(off3,off4);
CCMenuItemToggle *Left = CCMenuItemToggle::itemWithTarget(this, menu_selector(MyScene::swapL),LeftOn,LeftOff,NULL);
CCMenuItemToggle *Right = CCMenuItemToggle::itemWithTarget(this, menu_selector(MyScene::swapR),RightOn,RightOff,NULL);
CCMenu *Radio= CCMenu::menuWithItems(Left,Right,NULL);
Radio->alignItemsHorizontallyWithPadding(20);
Radio->setPosition(ccp(WinSize.width/2,WinSize.height/2));
this->addChild(Radio);
//////////////////////////////
return true;
}
void MyScene::swapL(CCObject *sender)
{
L= (CCMenuItemToggle*)sender;
CCLOG("L= %d",L->getSelectedIndex());
int i=(L->getSelectedIndex());
}
void MyScene::swapR(CCObject *sender)
{
R= (CCMenuItemToggle*)sender;
CCLOG("R= %d",R->getSelectedIndex());
int j=(R->getSelectedIndex());
}
Is it possible to have 2 void functions to send arguements to a third function one each from those 2 functions ?
Yes, It's possible, Why do you think it is not possible?
Online Sample:
#include<iostream>
void doSomething(int &i)
{
i = 10;
}
void doSomethingMore(int &j)
{
j = 20;
}
void FinalDoSomething(const int i, const int j, int &k)
{
k = i + j;
}
int main()
{
int i = 0;
doSomething(i);
int j = 0;
doSomethingMore(j);
int k = 0;
FinalDoSomething(i,j,k);
std::cout<<k;
return 0;
}
You could have a method between that calls. This function stores the first call value into a member and on the second call it calls the function you want to pass the two parameters (using the previously passed one + the member
all. I have some function "tetrisl" in this function I want to move tetris sprites down:
-(void)tetrisL:(ccTime)dt {
Tetris *s = [[Tetris node]initWithArraySize:4];
[s createL];
for (CCSprite *new in s.tetrisArray) {
[self addChild:new];
id actionMove = [CCMoveTo actionWithDuration:3 position:ccp(new.position.x,0)];
[new runAction: actionMove];
}
[s release];
}
But it's don't work. I think because I try to move different sprites in same Action. How can i fix it? Thanks
Here is Tetris class
#interface Tetris : CCNode {
NSMutableArray *tetrisArray;
Blocks *tempBlock;
}
#property (nonatomic, retain) NSMutableArray *tetrisArray;
#property (nonatomic, retain) Blocks *tempBlock;
-(id)initWithArraySize:(int)sz;
-(void)createL;
#implementation Tetris
#synthesize tetrisArray;
#synthesize tempBlock;
-(id)initWithArraySize:(int)sz {
if( (self=[super init] )) {
tetrisArray = [[NSMutableArray alloc]initWithCapacity:sz];
}
return self;
}
-(void)createL {
int xPos = 10;
int yPos = 460;
for (int i = 0; i < 4; i++) {
tempBlock = [[Blocks node]initWithType:1];
tempBlock.blockSprite.position = ccp(xPos,yPos);
[tetrisArray addObject:tempBlock.blockSprite];
xPos = xPos + 26;
[tempBlock release];
}
}
-(void)dealloc {
[tempBlock release];
[tetrisArray release];
[super dealloc];
}
you can't assign one action to different sprites. One action - one sprite. You can use action copy function to dublicate actions.
But in your case action creates in loop, so it must be different actions...
may be problem somewhere else.
Defferent sprites cann't execute the same action at the same time, so you should copy the action, like following code:
sprite->runAction((CCActionInterval*)aciotn->copy->autoRelease());
I wrote c++ class that return uiimage (I made sure that it doesn't return null) and I am trying to call it from objective c obe (uiview based classs) as below code, the problem is that no image be displayed any suggestion to solve that , also when put the code of c++ class at the objective c class the image appear
images imaging = new images(); // the c++ class .mm file
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES);
NSString *fullPath = [[paths lastObject] stringByAppendingPathComponent:ImageFileName];
NSLog(fullPath);
#try {
UIView* holderView = [[UIView alloc] initWithFrame:CGRectMake(0,0,self.view.bounds.size.width,self.view.bounds.size.height)];
viewer = [[UIImageView alloc] initWithFrame:[holderView frame]];
[viewer setImage:imaging->LoadImage(fullPath)];
viewer.contentMode = UIViewContentModeScaleAspectFit;
//holderView.contentMode = UIViewContentModeScaleAspectFit ;
[holderView addSubview:viewer];
[self.view addSubview:holderView];
}
#catch (NSException * e) {
NSLog(#"this was what happened: %a ",e);
}
this is a part of the loadimage, which create image from the buffer data
try{
...............
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL,MyFilter->GetOutput()->GetBufferPointer(), bufferLength, NULL);
if(colorSpaceRef == NULL)
{
NSLog(#"Error allocating color space");
CGDataProviderRelease(provider);
}
CGImageRef iref = CGImageCreate(Width,Height,bitsPerComponent,bitsPerPixel,bytesPerRow, colorSpaceRef,bitmapInfo,provider,NULL,YES,renderingIntent);
CGContextRef context = NULL;
if ((Comptype==itk::ImageIOBase::SHORT)||(Comptype==itk::ImageIOBase::USHORT))
{
context = CGBitmapContextCreate(MyFilter->GetOutput()->GetBufferPointer(),
Width,
Height,
bitsPerComponent,
bytesPerRow,
colorSpaceRef,
bitmapInfo);
}
else
{
context = CGBitmapContextCreate(myimg->GetBufferPointer(),
Width,
Height,
bitsPerComponent,
bytesPerRow,
colorSpaceRef,
bitmapInfo);
}
if(context == NULL)
{
NSLog(#"Error context not created");
}
//Create the UIImage to be displayed
UIImage * uiimage = nil;
if (context)
{
CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, Width, Height), iref);
CGImageRef imageRef = CGBitmapContextCreateImage(context);
//if([UIImage respondsToSelector:#selector(imageWithCGImage:scale:orientation:)]) {
//float scale = [[UIScreen mainScreen] scale];
//uiimage = [UIImage imageWithCGImage:imageRef scale:scale orientation:UIImageOrientationUp];
//} else {
uiimage = [UIImage imageWithCGImage:imageRef];
//}
CGImageRelease(imageRef);
CGContextRelease(context);
}
CGColorSpaceRelease(colorSpaceRef);
CGImageRelease(iref);
CGDataProviderRelease(provider);
return uiimage;
//return uiimage;
}
catch (itk::ExceptionObject & e)
{
throw(e);
}
The code looks okay - the Objective C, Objective C++ and C++ code should all work together fine, so I'd guess the problem may be within images::LoadImage(), even though it works separately - could we see that function?
Did you confirm that LoadImage returns UIImage object?
UIImage *image = imaging->LoadImage(fullPath)];
NSLog(#"className of image is \"%#\"", [image className]);
[viewer setImage:image];
Sometimes the Obj-C objects created by class functions in Cocoa are autoreleased. You may need to do:
uiimage = [UIImage imageWithCGImage:imageRef];
[uiimage retain];
Since this is on iOS I believe this is required, since iOS does not have garbage collection like OS X Cocoa.