How to make custom completion Handler with Objective-C - ios

I've looked through various documents, but don't know how to apply the completion handler in the following cases.
//example function.
//default textView height 100.0f
-(void)getHeightResult{
[self initHeight];
NSLog(#"%#", [self setTextManager]);
}
-(NSString *)setTextManager{
if(self.textView.frame.size.heigh != 100){
return #"new Height";
} else{
return #"Default Height;
}
}
-(void)initHeight{
int result;
for(int i = 0 ; i <= 20: i ++){
result = result + i;
}
//result = 210
self.textView.frame.size.height = result;
}
When executing the above functions, getHeightResult always outputs 'Default Height'.
How can I apply a Completion Handler to get it returned as the value calculated in initHeight from a source like this?
We ask for answers to help you understand the Completion Handler, not for asking the actual source.

Code for completionHandler,
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self getSize:^(bool status) {
if (status == true) {
// perofm your operioan
NSLog(#"Done");
}
}];
}
typedef void (^CompletionBlock)(_Bool status);
-(void)getSize:(CompletionBlock)block{
__block int count = 0;
[self initWidth:^(bool status) {
count++;
if (count == 2) {
block(true);
}
}];
[self initHeight:^(bool status) {
count++;
if (count == 2) {
block(true);
}
}];
}
-(void)initHeight:(CompletionBlock)block{
int result;
for(int i = 0 ; i <= 100; i ++) {
result = result + i;
}
block(true);
}
-(void)initWidth:(CompletionBlock)block{
int result;
for(int i = 0 ; i <= 20; i ++){
result = result + i;
}
block(true);
}

Related

avcodec_receive_packet() doesn't see the output

I'm trying to create a converter which will make a video out of set of images. Everything is at its place, AVFormatContext, AVCodecContext, AVCodec. I'm creating YUV AVFrame out of UIImage and send it to encoder by avcodec_send_frame() method. Everything goes fine until I'm trying to get AVPacket with method avcodec_receive_packet(). Every time it returns -53 which means - output is not available in the current state - user must try to send input. As I said, I'm sending an input before I'm trying to get something and sending is successful.
Here's my code:
Init ffmpeg entities:
- (BOOL)setupForConvert:(DummyFVPVideoFile *)videoFile outputPath:(NSString *)path
{
if (!videoFile) {
[self.delegate convertationFailed:#"VideoFile is nil!"];
return NO;
}
currentVideoFile = videoFile;
outputPath = path;
BOOL success = NO;
success = [self initFormatCtxAndCodecs:path];
if (!success) {
return NO;
}
success = [self addCameraStreams:videoFile];
if (!success) {
return NO;
}
success = [self openIOContext:path];
if (!success) {
return NO;
}
return YES;
}
- (BOOL)initFormatCtxAndCodecs:(NSString *)path
{
//AVOutputFormat *fmt = av_guess_format("mp4", NULL, NULL);
int ret = avformat_alloc_output_context2(&pFormatCtx, NULL, NULL, [path UTF8String]);
if (ret < 0) {
NSLog(#"Couldn't create output context");
return NO;
}
//encoder codec init
pCodec = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!pCodec) {
NSLog(#"Couldn't find a encoder codec!");
return NO;
}
pCodecCtx = avcodec_alloc_context3(pCodec);
if (!pCodecCtx) {
NSLog(#"Couldn't alloc encoder codec context!");
return NO;
}
pCodecCtx->codec_tag = AV_CODEC_ID_H264;
pCodecCtx->bit_rate = 400000;
pCodecCtx->width = currentVideoFile.size.width;
pCodecCtx->height = currentVideoFile.size.height;
pCodecCtx->time_base = (AVRational){1, (int)currentVideoFile.framerate};
pCodecCtx->framerate = (AVRational){(int)currentVideoFile.framerate, 1};
pCodecCtx->gop_size = 10;
pCodecCtx->max_b_frames = 1;
pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
NSLog(#"Couldn't open the encoder codec!");
return NO;
}
pPacket = av_packet_alloc();
return YES;
}
- (BOOL)addCameraStreams:(DummyFVPVideoFile *)videoFile
{
AVCodecParameters *params = avcodec_parameters_alloc();
if (!params) {
NSLog(#"Couldn't allocate codec parameters!");
return NO;
}
if (avcodec_parameters_from_context(params, pCodecCtx) < 0) {
NSLog(#"Couldn't copy parameters from context!");
return NO;
}
for (int i = 0; i < videoFile.idCameras.count - 1; i++)
{
NSString *path = [videoFile.url URLByAppendingPathComponent:videoFile.idCameras[i]].path;
AVStream *stream = avformat_new_stream(pFormatCtx, pCodec);
if (!stream) {
NSLog(#"Couldn't alloc stream!");
return NO;
}
if (avcodec_parameters_copy(stream->codecpar, params) < 0) {
NSLog(#"Couldn't copy parameters into stream!");
return NO;
}
stream->avg_frame_rate.num = videoFile.framerate;
stream->avg_frame_rate.den = 1;
stream->codecpar->codec_tag = 0; //some silly workaround
stream->index = i;
streams[path] = [[VideoStream alloc] initWithStream:stream];
}
return YES;
}
- (BOOL)openIOContext:(NSString *)path
{
AVIOContext *ioCtx = nil;
if (avio_open(&ioCtx, [path UTF8String], AVIO_FLAG_WRITE) < 0) {
return NO;
}
pFormatCtx->pb = ioCtx;
return YES;
}
And here's convertation process:
- (void)launchConvert:(DummyFVPVideoFile *)videoFile
{
BOOL convertInProgress = YES;
unsigned int frameCount = 1;
unsigned long pts = 0;
BOOL success = NO;
success = [self writeHeader];
if (!success) {
NSLog(#"Couldn't write header!");
return;
}
AVRational defaultTimeBase;
defaultTimeBase.num = 1;
defaultTimeBase.den = videoFile.framerate;
AVRational streamTimeBase = streams.allValues.firstObject.stream->time_base;
while (convertInProgress)
{
pts += av_rescale_q(1, defaultTimeBase, streamTimeBase);
for (NSString *path in streams.allKeys)
{
UIImage *img = [UIImage imageWithContentsOfFile:[NSString stringWithFormat:#"%#/%u.jpg", path, frameCount]];
AVPacket *pkt = [self getAVPacket:img withPts:pts];
if (!pkt->data) { continue; }
pkt->stream_index = streams[path].stream->index;
//check all settings of pkt
if (![self writePacket:pkt]) {
NSLog(#"Couldn't write packet!");
convertInProgress = NO;
break;
}
}
frameCount++;
}
success = [self writeTrailer];
if (!success) {
NSLog(#"Couldn't write trailer!");
return;
}
NSLog(#"Convertation finished!");
//delegate convertationFinished method
}
- (BOOL)writeHeader
{
if (avformat_write_header(pFormatCtx, NULL) < 0) {
return NO;
}
return YES;
}
- (BOOL)writePacket:(AVPacket *)pkt
{
if (av_interleaved_write_frame(pFormatCtx, pkt) != 0) {
return NO;
}
return YES;
}
- (BOOL)writeTrailer
{
if (av_write_trailer(pFormatCtx) != 0) {
return NO;
}
return YES;
}
/**
This method will create AVPacket out of UIImage.
#return AVPacket
*/
- (AVPacket *)getAVPacket:(UIImage *)img withPts:(unsigned long)pts
{
if (!img) {
NSLog(#"imgData is nil!");
return nil;
}
uint8_t *imgData = [self getPixelDataFromImage:img];
AVFrame *frame_yuv = av_frame_alloc();
if (!frame_yuv) {
NSLog(#"frame_yuv is nil!");
return nil;
}
frame_yuv->format = AV_PIX_FMT_YUV420P;
frame_yuv->width = (int)img.size.width;
frame_yuv->height = (int)img.size.height;
int ret = av_image_alloc(frame_yuv->data,
frame_yuv->linesize,
frame_yuv->width,
frame_yuv->height,
frame_yuv->format,
32);
if (ret < 0) {
NSLog(#"Couldn't alloc yuv frame!");
return nil;
}
struct SwsContext *sws_ctx = nil;
sws_ctx = sws_getContext((int)img.size.width, (int)img.size.height, AV_PIX_FMT_RGB24,
(int)img.size.width, (int)img.size.height, AV_PIX_FMT_YUV420P,
0, NULL, NULL, NULL);
const uint8_t *scaleData[1] = { imgData };
int inLineSize[1] = { 4 * img.size.width };
sws_scale(sws_ctx, scaleData, inLineSize, 0, (int)img.size.height, frame_yuv->data, frame_yuv->linesize);
frame_yuv->pict_type = AV_PICTURE_TYPE_I;
frame_yuv->pts = pCodecCtx->frame_number;
ret = avcodec_send_frame(pCodecCtx, frame_yuv); //every time everything is fine
if (ret != 0) {
NSLog(#"Couldn't send yuv frame!");
return nil;
}
av_init_packet(pPacket);
pPacket->dts = pPacket->pts = pts;
do {
ret = avcodec_receive_packet(pCodecCtx, pPacket); //every time -35 error
NSLog(#"ret = %d", ret);
if (ret == AVERROR_EOF) {
NSLog(#"AVERROR_EOF!");
} else if (ret == AVERROR(EAGAIN)) {
NSLog(#"AVERROR(EAGAIN)");
} else if (ret == AVERROR(EINVAL)) {
NSLog(#"AVERROR(EINVAL)");
}
if (ret != 0) {
NSLog(#"Couldn't receive packet!");
//return nil;
}
} while ( ret == 0 );
free(imgData);
av_packet_unref(pPacket);
av_packet_free(pPacket);
av_frame_unref(&frame_yuv);
av_frame_free(&frame_yuv);
//perform other clean up and test dat shit
return pPacket;
}
Any insights would be helpful. Thanks!
There may be two reasons.
According to on of the documents of FFmpeg you may need to feed more then one packet to avcodec_send_frame() to receive packet return successful.
I cannot confirm that you allocated enough sized buffer for pPacket. The functions av_packet_alloc() and av_init_packet() won't allocate any buffer but latter sets it to NULL instead. So allocation must be done after init. Somewhere you should allocate buffer either manually or with av_new_packet(pPacket, SIZE).
Hope that helps.

how to get the uibutton state value in test.m file

In .m file i have methods like
-(void) textdata
{
long i = search.text.length;
if (i > 0) {
searchButton.enabled = YES;
}
else
{
searchButton.enabled = NO;
}
[self buttonstate];
}
-(int) buttonstate
{
if ([searchButton isEnabled]) {
j = 1;
}
else
j = 0;
NSLog(#" j value is %d",j);
return j;
}
- (void) textFieldDidChange
{
[self textdata];
NSLog(#"test data is %#",search.text);
}
And in the tests.m file i have test like
-(void) testwithoutData
{
myapiViewController *apiViw = [[myapiViewController alloc]init];
[apiViw textdata];
int kn = [apiViw buttonstate];
NSLog(#"the value is %ld",(long)kn);
}
You have to do alloc init because due to this instance variable getting nil. Try this -
In ViewController.m -
-(int) buttonstate {
long i = self.searchTxt.text.length;
if (i > 0) {
self.searchBtn.enabled = YES;
}
else
{
self.searchBtn.enabled = NO;
}
if ([self.searchBtn isEnabled]) {
j = 1;
}
else
j = 0;
NSLog(#" j value is %d",j);
return j;
}
In ViewControllerTests.m -
- (void)testExample {
// This is an example of a functional test case.
XCTAssert(YES, #"Pass");
self.vcToTest.searchTxt = [[UITextField alloc] init];
self.vcToTest.searchBtn = [[UIButton alloc] init];
self.vcToTest.searchTxt.text = #"test";
int kn = [self.vcToTest buttonstate];
NSLog(#"the value is %ld",(long)kn);
}
Check the attached screenshot output -

Why iOS AudioQueue memory constantly increasing?

I hava a Play audio class used AudioToolBox.framework ,AudioQueue.
I encountered a problem, every piece of audio data playback, the memory will be increased, after playback is complete, the memory will not be reduced. If the batch test, it will be added to the hundreds of megabytes of memory, I want to know what causes memory has been increased, the audio data on each pass of each object is released or other reasons.
Here is my playThread class code:
#interface PlayThread()
{
BOOL transferDataComplete; // if thers is no data transfer to playthread set transferDataComplete = yes;
NSMutableArray *receiveDataArray;// audio data array
BOOL isPlay;// if audioqueue start,isPlay = yes,
}
#end
#pragma mark class implementation
#implementation PlayThread
- (instancetype)init
{
if (self = [super init]) {
receiveDataArray = [[NSMutableArray alloc]init];
isPlay = NO;
transferDataComplete = false;
bufferOverCount = QUEUE_BUFFER_SIZE;
audioQueue = nil;
}
return self;
}
// audio queue callback function
static void BufferCallback(void *inUserData,AudioQueueRef inAQ,AudioQueueBufferRef buffer)
{
USCPlayThread* player=(__bridge USCPlayThread*)inUserData;
[player fillBuffer:inAQ queueBuffer:buffer];
}
// fill buffer
-(void)fillBuffer:(AudioQueueRef)queue queueBuffer:(AudioQueueBufferRef)buffer
{
while (true){
NSData *audioData = [self getAudioData];
if( transferDataComplete && audioData == nil) {
bufferOverCount --;
break;
}
else if(audioData != nil){
memcpy(buffer->mAudioData, [audioData bytes] , audioData.length);
buffer->mAudioDataByteSize = (UInt32)audioData.length;
AudioQueueEnqueueBuffer(queue, buffer, 0, NULL);
break;
}
else
break;
} // while
if(bufferOverCount == 0){
// stop audioqueue
[self stopAudioQueue];
dispatch_async(dispatch_get_main_queue(), ^{
if ([self.delegate respondsToSelector:#selector(playComplete)]) {
[self.delegate playComplete];
}
});
}
}
-(void)addPlayData:(NSData *)data
{
NSUInteger count = 0;
#synchronized(receiveDataArray){
[receiveDataArray addObject:data];
}
}
/**
* get data from receiveDataArray
*/
-(NSData*)getAudioData
{
NSData *headData = nil;
#synchronized(receiveDataArray){
if(receiveDataArray.count > 0){
headData = [receiveDataArray objectAtIndex:0];
[receiveDataArray removeObjectAtIndex:0];
}
}
return headData;
}
- (void)startPlay // start audioqueue to play audio data
{
[self reset];
[self open];
for(int i=0; i<QUEUE_BUFFER_SIZE; i++)
{
[self fillBuffer:audioQueue queueBuffer:audioQueueBuffers[i]];
}
// audioqueuestart
AudioQueueStart(audioQueue, NULL);
#synchronized(self){
isPlay = YES;
}
if ([self.delegate respondsToSelector:#selector(playBegin)]) {
[self.delegate playBegin];
}
}
-(void)createAudioQueue
{
if (audioQueue) {
return;
}
AudioQueueNewOutput(&audioDescription, BufferCallback, (__bridge void *)(self), nil, nil, 0, &audioQueue);
if(audioQueue){
for(int i=0;i<QUEUE_BUFFER_SIZE;i++){
AudioQueueAllocateBufferWithPacketDescriptions(audioQueue, EVERY_READ_LENGTH, 0, &audioQueueBuffers[i]);
}
}
}
-(void)stopAudioQueue
{
if(audioQueue == nil){
return;
}
#synchronized(self){
if(isPlay){
isPlay = NO;
}
}
AudioQueueStop(audioQueue, TRUE);
}
-(void)setAudioFormat
{
audioDescription.mSampleRate = 16000;
audioDescription.mFormatID = kAudioFormatLinearPCM;
audioDescription.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
audioDescription.mChannelsPerFrame = 1;
audioDescription.mFramesPerPacket = 1;
audioDescription.mBitsPerChannel = 16;
audioDescription.mBytesPerFrame = (audioDescription.mBitsPerChannel/8) * audioDescription.mChannelsPerFrame;
audioDescription.mBytesPerPacket = audioDescription.mBytesPerFrame ;
}
-(void)close
{
if (audioQueue) {
AudioQueueStop(audioQueue, true);
AudioQueueDispose(audioQueue, true);
audioQueue = nil;
isPlay = NO;
}
}
-(BOOL)open {
if([self isOpen]){
return YES;
}
[self close];
[self setAudioFormat];
[self createAudioQueue];
return YES;
}
-(BOOL)isOpen
{
return (audioQueue != nil);
}
- (void)reset
{
bufferOverCount = QUEUE_BUFFER_SIZE;
transferDataComplete = NO;
}
- (BOOL)isPlaying
{
return isPlay;
}
- (void)disposeQueue
{
if (audioQueue) {
AudioQueueDispose(audioQueue, YES);
}
audioQueue = nil;
}
- (void)dealloc
{
[self disposeQueue];
}
Here is ViewContrller.m :
- (void)viewDidLoad {
[super viewDidLoad];
PlayThread *playThread = [[PlayThread alloc]init];
playThread.delegate = self;
self.playThread = playThread;
for (int i = 0; i < 10; i++)
{ // create empth audio data to simulate
NSMutableData *data = [[NSMutableData alloc]initWithLength:10000];
[self.playThread addPlayData:data];
}
[self.playThread startPlay];
}
Here is PlayThread's delegate method:
// When the play completely,then play once again,memory will continue to increase
- (void)playComplete
{
dispatch_async(dispatch_get_main_queue(), ^{
for (int i = 0; i < 10; i++)
{
NSMutableData *data = [[NSMutableData alloc]initWithLength:10000];
[self.playThread addPlayData:data];
}
[self.playThread startPlay];
});
}
Why memory has continued to increase, how can promptly release memory?
AudioQueueNewOutput(&audioDescription, BufferCallback, (__bridge void *)(self), nil, nil, 0, &audioQueue);
here parameter can not be nil

How to avoid retain cycle warning when a block try to set its self to nil?

I'm reading the source code of SimpleAudioPlayer and find that Xcode will raise a warning when playBlock try to set its self nil.
I don't know how to get rid of this warning, or must we set playBlock to nil?
- (void) playFiles:(NSArray*) filesList withCompletionBlock:(CompletionBlock) completion
{
__block int idx = 0;
__block void(^playBlock)();
playBlock = ^() {
if (idx >= filesList.count) {
if (completion) {
completion ( YES );
}
playBlock = nil; //Capturing 'playBlock' strongly in this block is likely to lead to a retain cycle
return ;
}
[self playFile:filesList[idx] withCompletionBlock:^(BOOL completed) {
playBlock ();
}];
idx ++;
};
playBlock ();
}
No need to set playBlock = nil; neither declare it with __block modifier. Modified code (see below) compiled without warnings or errors.
- (void) playFiles:(NSArray*) filesList withCompletionBlock:(CompletionBlock) completion
{
__block int idx = 0;
void(^playBlock)();
playBlock = ^() {
if (idx >= filesList.count) {
if (completion) {
completion ( YES );
}
return ;
}
[self playFile:filesList[idx] withCompletionBlock:^(BOOL completed) {
playBlock ();
}];
idx ++;
};
playBlock ();
}

Dead code with Xcode anaylser

i wanted to analyse my projet, and xcode analyser find some Dead Code in my rootcontroller
Xcode tell me that :
Dead code
The left operand to '+' is always 0
-> Variable 'i' initialized to 0
-> The left operand to '+' is always 0
Can someone explain me this ans how to clean that code, thanks....
here is my rootcontroller.m
#import "RootViewController22.h"
#import "LabelCell.h"
#import "NibLoadedCell.h"
#import "GradientBackgroundTable.h"
#import "NibLoadedCell2.h"
#import "NibLoadedCell3.h"
#implementation RootViewController22
//
// title
//
// returns the navigation bar text for the front screen
//
/*- (NSString *)title
{
return NSLocalizedString(#"Les Robes du Bengal", #"");
}*/
//
// createRows
//
// Constructs all the rows on the front screen and animates them in
//
- (void)createRows
{
[self addSectionAtIndex:0 withAnimation:UITableViewRowAnimationFade];
for (NSInteger i = 0; i < 1; i++)
{
[self
appendRowToSection:0
cellClass:[NibLoadedCell2 class]
cellData:[NSString stringWithFormat:
NSLocalizedString(#"This is row %ld", #""), i + 1]
withAnimation:(i % 2) == 0 ?
UITableViewRowAnimationLeft :
UITableViewRowAnimationRight];
}
//[self addSectionAtIndex:0 withAnimation:UITableViewRowAnimationFade];
//for (NSInteger i = 0; i < 4; i++)
//{
// [self
// appendRowToSection:0
// cellClass:[LabelCell class]
// cellData:[NSString stringWithFormat:
// NSLocalizedString(#"This is row %ld", #""), i + 1]
// withAnimation:(i % 2) == 0 ?
// UITableViewRowAnimationLeft :
// UITableViewRowAnimationRight];
// }
[self addSectionAtIndex:1 withAnimation:UITableViewRowAnimationFade];
for (NSInteger i = 0; i < 1; i++)
{
[self
appendRowToSection:1
cellClass:[NibLoadedCell3 class]
cellData:[NSString stringWithFormat:
NSLocalizedString(#"This is row %ld", #""), i + 1]
withAnimation:(i % 2) == 0 ?
UITableViewRowAnimationLeft :
UITableViewRowAnimationRight];
}
// [self addSectionAtIndex:2 withAnimation:UITableViewRowAnimationFade];
// for (NSInteger i = 0; i < 4; i++)
// {
// [self
// appendRowToSection:2
// cellClass:[TextFieldCell class]
// cellData:
// [NSMutableDictionary dictionaryWithObjectsAndKeys:
// [NSString stringWithFormat:
// NSLocalizedString(#"TextField %ld", #""), i + 1],
// #"label",
// #"", #"value",
// NSLocalizedString(#"Value goes here", #""),
// #"placeholder",
// nil]
// withAnimation:(i % 2) == 0 ?
// UITableViewRowAnimationLeft :
// UITableViewRowAnimationRight];
// }
[self addSectionAtIndex:2 withAnimation:UITableViewRowAnimationFade];
for (NSInteger i = 0; i < 1; i++)
{
[self
appendRowToSection:2
cellClass:[NibLoadedCell class]
cellData:[NSString stringWithFormat:
NSLocalizedString(#"This is row %ld", #""), i + 1]
withAnimation:(i % 2) == 0 ?
UITableViewRowAnimationLeft :
UITableViewRowAnimationRight];
}
[self hideLoadingIndicator];
}
//
// refresh
//
// Removes all existing rows and starts a reload (on a 0.5 second timer)
//
- (void)refresh:(id)sender
{
[self removeAllSectionsWithAnimation:UITableViewRowAnimationFade];
[self performSelector:#selector(createRows) withObject:nil afterDelay:0.5];
[self showLoadingIndicator];
}
//
// viewDidLoad
//
// On load, refreshes the view (to load the rows)
//
- (void)viewDidLoad
{
self.navigationController.navigationBar.tintColor = [UIColor blackColor];
self.title = #"Les Robes";
[super viewDidLoad];
self.useCustomHeaders = YES;
[self refresh:nil];
}
//
// loadView
//
// Since the view is so simple (just a GradientBackgroundView) we might as
// well contruct it in code.
//
- (void)loadView
{
GradientBackgroundTable *aTableView =
[[[GradientBackgroundTable alloc]
initWithFrame:CGRectZero
style:UITableViewStyleGrouped]
autorelease];
self.view = aTableView;
self.tableView = aTableView;
}
//
// textFieldDidEndEditing:
//
// Update the rowData for the text field rows to match the edited value of the
// text field.
//
//
// tableView:titleForHeaderInSection:
//
// Header text for the three sections
//
// Parameters:
// aTableView - the table
// section - the section for which header text should be returned
//
// returns the header text for the appropriate section
//
- (NSString *)tableView:(UITableView *)aTableView
titleForHeaderInSection:(NSInteger)section
{
if (section == 0)
{
return NSLocalizedString(#"Les Motifs", nil);
}
else if (section == 1)
{
return NSLocalizedString(#"Les Couleurs", nil);
}
else if (section == 2)
{
return NSLocalizedString(#"À Savoir", nil);
}
return nil;
}
#end
In your createRows method, you have a loop that will only execute once:
for (NSInteger i = 0; i < 1; i++) { ... }
Therefore, things like i + 1 and i % 2 could just be constants since i will always be 0.
for (NSInteger i = 0; i < 1; i++)
Your for loop starts at 0, ends at 0. i++ will never be executed.
You've got several loops of the form:
for (NSInteger i = 0; i < 1; i++) { }
That code is nearly meaningless, though... the code in the body of the loop will only ever execute once, while i == 0, so you might as well just take it out of the loop.

Resources