Tampilkan postingan dengan label Objective-C X-Code. Tampilkan semua postingan
Tampilkan postingan dengan label Objective-C X-Code. Tampilkan semua postingan

Stupid XCode: XCode can not run using the selected device

I got the following error out of sudden:


I swear I did not touch the project settings! It had run well the previous time, but not anymore now! I did a google search and it baffled me to read how many problems could cause this and what the possible solutions are.

I finally nailed it: it was caused by a key in info.plist file which had no value: "Icon file". Below it is a another key: "Icon files" having value of an icons array.

I deleted that Icon file key and the project runs well again. But that problem could have easily been caused by many other factors.... the error message does not help at all!

Comparison of memory footprints of different textures available in Cocos2d

So I have been fiddling with textures in Cocos2d to find out what image format produces the least memory foot print while not degrading quality in a very visible manner.

So I created an original PNG image, and then converted it to different image formats using Texture Packer. I then created an XCode project to load those images and print out the memory footprint. The XCode project can be fetched from: https://github.com/rumahfirman/texture-format

First, I will list image format from biggest memory foot print to smallest memory foot print (the original image is original.png which is RGBA 8888):


"original.png" file size=5 KB rc=2 id=2 1024 x 1024 @ 32 bpp = 4096 KB 
"rgba4444.png" file size=4 KB rc=2 id=3 1024 x 1024 @ 32 bpp = 4096 KB
"rgba4444fsa.png" file size=131 KB rc=2 id=4 1024 x 1024 @ 32 bpp = 4096 KB
"rgb888.pvr.ccz" file size=2 KB rc=2 id=8 640 x 960 @ 32 bpp = 2400 KB
"rgba4444fsa.pvr.ccz" file size=90 KB rc=2 id=9 1024 x 1024 @ 16 bpp = 2048 KB
"rgba4444fs.pvr.ccz" file size=16 KB rc=2 id=10 1024 x 1024 @ 16 bpp = 2048 KB
"rgba5551fs.pvr.ccz" file size=50 KB rc=2 id=11 1024 x 1024 @ 16 bpp = 2048 KB
"rgba5551fsa.pvr.ccz" file size=120 KB rc=2 id=12 1024 x 1024 @ 16 bpp = 2048 KB
"rgb565.pvr.ccz" file size=140 KB rc=2 id=7 640 x 960 @ 16 bpp = 1200 KB
"pvrtc4.pvr.ccz" file size=20 KB rc=2 id=6 1024 x 1024 @ 4 bpp = 512 KB
"pvrtc2.pvr.ccz" file size=16 KB rc=2 id=5 1024 x 1024 @ 2 bpp = 256 KB

From the above data, it can be seen that file size does not correlate to memory footprint. The smallest file size (which is 4 KB) produces the biggest memory foot print (4096 KB).

It is also obvious that the memory footprint of RGBA 4444 uncompressed PNG is the same as the original image which is RGBA 8888. So RGBA 4444 does not produce memory saving, yet result in quality degradation.

In the above file name, the suffix fs stands for Floyd Steinberg dithering. FSA stands for Floyd Steinberg Alpha dithering.

Dithering basically means to smoothen your image. If your original image contains color gradation, dithering is especially important. Reducing image quality often results in abrupt color changes while in the original image, the color gradually changes. This can be rectified by dithering and FS works well.

If your original image contains alpha channel, FSA also dithers the alpha channel, creating "dotted artifacts", which is especially visible if your image z-order is higher than some other image, i.e there is an image located behind the dithered image. To rectify this, choose FS without A, i.e dither all channels in the image except the alpha channel.

Now let's talk about quality. From best to worse:
Original
RGB 888
RGBA 5551
RGBA 4444
RGB 565

Rule of thumb: if possible, always use RGB 565 for background images.

PVR Texture Format, Texture Cache

I have been looking around for tools to make my sprite sheet smaller. People say that PVR textures are the way to go.

So the graphics processor on iPhone is a design licensed from Power VR  (http://en.wikipedia.org/wiki/PowerVR). PVR textures are in such a format that Power VR graphics chips can easily digest, resulting in much faster processing. And smaller file size.

Sometime ago I bought Vladu Bogdan's SpriteHelper and LevelHelper toolsuite. In SpriteHelper, there is an option to save the resulting sprite sheet into a PVR format. But the quality is so low (it's lossily compressed). I'm looking for a tool that enables me to reduce the size and memory footprint of my sprite sheets without the quality degradation (lossless compression or uncompressed).

I used Level Helper in just one project that needed it. It's ok. I use Sprite Helper quite a lot but recently with the introduction of the Gatekeeper for Mac, it has become increasingly a pain to use. Really. It just frustrates me how Sprite Helper gets stuck at the beach ball of death phase when starting up. How it fails to save the plist, how it won't add  multiple images at once, etc. I have just grown sick of it now.

Anyway, back to PVR.

XCode comes with a tool to convert sprite sheet into PVR texture, but just as SpriteHelper, it uses lossy compression. The tool can be found in dir: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/texturetool

It's called texturetool.

Another tool: TexturePacker. I read people praising it, but it's paid app. Haven't tried.

Then there is this tool from Power VR themselves: PVRTexTool. It comes with both GU and CL interfaces. But I have not managed to get it working. I mean, I can't get it to convert my PNG files (generated by Sprite Helper) into pvr format.

So I fall back to texturetool. I created a small bash script to convert all PNG files in a directory into PVR format. Here it is:


#!/bin/bash

for file in `find ./ -name "*png"`
do
texturetool  -e PVRTC --bits-per-pixel-4 -f PVR -m  -o $file.pvr $file
done


The image quality is lower than the original PNG and in some cases the difference stands out. But I have not got much of a choice right now. Will tinker with PVRTexTool later when I got time.

I also learn about how sprite frame cache and texture cache work in Cocos2D. So basically texture cache is a dictionary with keys being the file/texture names and values the textures. One texture can (and should) contain a whole slew of images. Not just one. And you access each image by sprite frame cache.

So for instance you have image1.png, image2.png.... image50.png. You pack them into one big image using Sprite Helper or other tools. And then you have another file describing the position/coordinate of each image. This file is called the plist file. It's a dictionary with key being the individual image name (image1, etc) and value the coordinate of it in the big, packed image.

You create sprite frame cache from the plist (i.e you pass the plist name as the argument into sprite frame cache static methods). Then sprite frame cache will try to load the image with the same name as the plist into the texture cache. Sprite frame cache will expect the image to end with the png extension. If you have different image extension, such as pvr, you need to load the image manually into the texture  cache. Code snippet:


    CCSpriteFrameCache *frameCache = [CCSpriteFrameCache sharedSpriteFrameCache];
    NSString *spriteFrameName = @"myplistfile.plist";
    NSString *textureName = @"myspritesheet.pvr";
    CCTexture2D *texture = [[CCTextureCache sharedTextureCache] textureForKey:textureName];
    [frameCache addSpriteFramesWithFile:spriteFrameName texture:texture];




Compile linphone for iPhone

My environment is Mac OS X Lion, newest macports (version 2.1.2). I followed the README down to each letter but errors were returned. The tools in the newest version of Macports have different requirements than the older ones used to write the README.

I then submitted a question to the linphone mailing list and got an answer. As follow:

Arek,

I've been struggling with this for days. It's mystifying to me that it was so hard to get working. I'm using Mountain Lion, Xcode 4.5, and iOS 6. I wonder if perhaps MacPorts is now installing newer versions of the tools required by the install guide.

At any rate, I just got Linphone fully built earlier today. To fix your 'install-sh' problem, you just need to create an empty file that is required by newer versions of autoconf:
> 'touch submodules/linphone/install-sh

At this point, I was stumped for a very long time by another error: 
> ./configure: line 4543: syntax error near unexpected token `HAVE_MD5SUM,test'
> ./configure: line 4543: `AM_CONDITIONAL(HAVE_MD5SUM,test -n $MD5SUM)'
As it turned out, the problem was again triggered by a line in configure.ac:
> configure.ac:42: error: 'AM_PROG_CC_STDC': this macro is obsolete.

The solution was to edit submodules/linphone/configure.ac and delete the line that says:
AM_PROG_CC_STDC
You will need to 'make veryclean' or run 'cd build/linphone; ./autogen.sh'

Incidentally, while working through this I also figured out the necessary steps to replace MacPorts with brew which has the advantage of being more self-contained and easily reversible than MacPorts.
1) Install brew: (instructions at http://mxcl.github.com/homebrew/)
2) Install dependencies:
brew install coreutils automake autoconf libtool intltool 
brew install wget pkgconfig cmake gmake yasm doxygen ImageMagick optipng
brew tap homebrew/dupes
brew install xz homebrew/dupes/grep

Linphone is hardcoded to look for a lot of tools in MacPorts-specific locations, (e.g. /opt/local/bin) so I also had to create a link from there to brew's standard install location: 'ln -s /usr/local /opt/local'

Good luck!
Eli

On Jan 15, 2013, at 12:00 PM, linphone-developers-request@nongnu.org wrote:

> Message: 1
> Date: Tue, 15 Jan 2013 16:08:36 +0800 (SGT)
> From: Firman 
> To: linphone-developers@nongnu.org
> Subject: [Linphone-developers] liblinphone SDK build failed.
> Message-ID:
>     <1358237316.44153.YahooMailClassic@web193306.mail.sg3.yahoo.com>
> Content-Type: text/plain; charset="iso-8859-1"

> Hi everybody,
> I have sifted through the mailing list archives but have not found anyone posting the same build failure problem as mine.

> So I cloned the git repo for iPhone
> git clone 
> git://git.linphone.org/linphone-iphone.git?--recursive

> and then followed everything that the README file says about how to build the liblinphone SDK down to each letter. And then I do

> sudo make all.

> It failed first with the following error:

> Generating configuration files for Speex, please wait....
> ? aclocal 
> configure.ac:5: error: 'AM_CONFIG_HEADER': this macro is obsolete.
> ??? You should use the 'AC_CONFIG_HEADERS' macro instead.
> /opt/local/share/aclocal-1.13/obsolete-err.m4:12: AM_CONFIG_HEADER is expanded from...
> configure.ac:5: the top level
> autom4te: /opt/local/bin/gm4 failed with exit status: 1
> aclocal: error: echo failed with exit status: 1
> make[1]: *** [/Users/barablu/Documents/workspace/liniphone/linphone-iphone/submodules/build/..//externals/speex/configure] Error 1
> make: *** [build] Error 2

> I then changed line 5 of configure.ac in speex directory from 
> AM_CONFIG_HEADER to AC_CONFIG_HEADERS. That error went away but now I get the following error that I have no clue how to solve. Can anybody please help me? I'm using XCode 4.5, iOS SDK 6, Mac OS X Lion. Thank you.

> Best regards,
> Arek Malang

> configure: loading site script /Users/barablu/Documents/workspace/liniphone/linphone-iphone/submodules/build/..//build/iphone-config.site
> Loading config.site for iPhone platform=Simulator version=4.0
> /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.3.sdk
> /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator3.2.sdk
> /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.0.sdk
> /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.1.sdk
> /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.2.sdk
> /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk
> Selecting SDK path = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk
> configure: error: cannot find install-sh, install.sh, or shtool in "/Users/barablu/Documents/workspace/liniphone/linphone-iphone/submodules/build/..//linphone" "/Users/barablu/Documents/workspace/liniphone/linphone-iphone/submodules/build/..//linphone/.." "/Users/barablu/Documents/workspace/liniphone/linphone-iphone/submodules/build/..//linphone/../.."
> make[1]: *** [/Users/barablu/Documents/workspace/liniphone/linphone-iphone/submodules/build/../build-i386-apple-darwin/linphone/Makefile] Error 1
> make: *** [build] Error 2

Objective C memory management

After a while finally (I think) I grasp how this memory management thing works in Objective C.

First about assign and retain.

When you declare properties using (retain) then the retain count will be incremented by one when you assign value to it.

@interface LayerA : CCLayer{

   CCSprite *sprite;
}

@property (retain) CCSprite *sprite;
@end

Now if in other classes you call:

LayerA.sprite = [CCSprite node];

The retain count of sprite in LayerA will be incremented by one and as such, it won't be released until you call:

LayerA.sprite = nil;

which will decrease the retain count by one. So everytime you assign a value to a retain property, you must have a matching nil assignment to it.

This can complicate manual memory management and I usually prefer to have assign instead of property.


@interface LayerA : CCLayer{

   CCSprite *sprite;
}

@property (assign) CCSprite *sprite;
@end

Now when you assign a value to the sprite property, the retain count will not be incremented.

Another case about memory management that I learnt the hard way was when passing argument to the class constructor.

When class A calls the constructor of class B and passes parameters, the parameters will be released when class A is released. So if class B caches those parameters, it is good only as long as class A lives. The constructor in class B needs to make deep copy of the parameter if class B wants to use the parameters in methods other than constructor.

Apple Code Signing

I was just released from being mired in litany of predicaments due to code signing procedures and here's my story...

So I work on a new macbook, someone else's, and i fail to code sign my app before deploying it to my device.

After two days I finally figured it out. (or better put: people sharing their knowledge on internet got it figured out).

Basically, code signing process is following:

You need to have the following in your computer when code sign:
- public and private key pair.
- certificate issued by you (if you are administrator of your Apple developer program)
- Apple intermediate certificate.

You got to have public and private key for code signing. This pair is created on your computer when you created certificates and provisioning profile FOR THE FIRST TIME. If you move development to other machine, you will have to do something to move this pair to the new machine.... The easiest way would be to re-create everything from scratch: delete your certificate in developer.apple.com and recreate it by submitting new certificate request from keychain.

If you develop on a new machine, the private key is usually missing. You can tell by going to the keychain. Your certificate should have a little triangle next to it, that you can click and it will show you the public and private key pair. If you don't have that triangle, you have the certificate but not the key pair and your code signing won't work.

The intermediate certificate can be downloaded from the the usual place in developer site without having to submit certificate request first. If you don't install this, you'll get this error from XCode: /usr/bin/codesign fail or something similar.

Code signing is one part. Then there's this thing called provisioning profile which basically says which certificate to sign which app and which app can be deployed on which device...

Er... I'm unsure. But that's my impression of two days fixing code signing error.

One more thing, I deleted all passwords in keychain because I used my own apple credential in XCode automatic refresh and now I wanted to use someone else's. I couldn't find the right password for XCode in keychain so I deleted them all. .-))
It worked. I was asked to enter new credential when I clicked refresh in XCode organizer. But another problem arose: When I created certificate request it gave me errors like incorrect passphrase or something. I could not create certificate request because I deleted all of my passwords! Solution is simple: restart the computer!

Check whether a string contains substring.

Turns out that the Foundation Framework does not have this nifty little utility to check whether a string contains a substring. But this can be easily done manually:


-(BOOL)string:(NSString*)string1 containsString:(NSString*)string2{
    NSRange rangeValue = [string1 rangeOfString:string2 options:NSCaseInsensitiveSearch];
    if (rangeValue.length > 0){
        return TRUE;
    }
    else {
        return FALSE;
    }
}

XCode fails to launch app on the device

So I hit the run button to run the app on the device, but instead of running on the device, I got this dialog:


XCode failed to run my app on my device. I checked my device: the app is uploaded alright, and when I clicked it, it ran well. But why did XCode give me that fail dialog?

I did the following to fix:

1. Go to the dir mentioned in the dialog. Delete the app directory (OpenGLES_Ch2_.....)
2. Delete the uploaded app on the device
3. Restart XCode.

And everything went fine again.

Small notes

Some lessons I learnt the past few days:

Cocos2D: Don't load heavy resources in the init method. This will crash the app. Instead, load them in a scheduled method.
Box2D: Don't change scene while world is locked (when detecting and responding to touch). This will cause a memory leak. Instead, change the scene in an scheduled method.
Android: The method insert in SQLiteDatabase doesn't throw exception. It does throw exception and print the stack trace but it's internally handled by that method and the exception won't by caught by the try catch block. If you need to catch exception, use insertOrThrow instead.

Code snippet to pad numbers with zero:

        NSString *paddingFormat = [[NSString stringWithFormat:@"time\n%%0%dd:%%0%dd", 2, 2] retain];
        NSString *paddedNumber = [NSString stringWithFormat:paddingFormat, 0, 0];

Result:

time
00:00

NIB files (or XIB files)

So, finally I got the hang of NIB files. I can now develop simple app either without the interface builder (pure code) or with interface builder (with NIB files).

I had always thought that a NIB file was always associated to a UIViewController. This was because when you create UIViewController, there is an option whether you want to create a NIB file for it or not. If you choose to create the associated NIB file, then the file owner of the NIB file will be set to the UIViewController class.

That was until I found this sample code from Apple: http://developer.apple.com/library/ios/#samplecode/Scrolling/Introduction/Intro.html#//apple_ref/doc/uid/DTS40008023

In that sample code, the class of the file owner is UIApplication. It turns out that you can actually develop your app from scratch using the interface builder only (well, almost). You connect the NIB file (the main NIB file) to your application by setting the key "main NIB file base name" in your plist file to the value of your NIB file name (without the XIB extension). Contrast this with the practice of calling [UIViewController alloc] initWithNibFile] when you create a NIB file for a UIViewController.

Just remember to always set the file owner of your NIB file!

Objective C Memory Management

I found two concise but complete references online:
http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/MemoryMgmt/Articles/MemoryMgmt.html
http://interfacelab.com/objective-c-memory-management-for-lazy-people/
http://www.raywenderlich.com/2657/memory-management-in-objective-c-tutorial

I will summarize them here to reinforce my understanding:

  1. If you do alloc + init or new (which is basically just a shortcut for alloc + init), you must subsequently call release.
  2. After you call release, you should set the var name to nil. So: [someVar release], and then someVar = nil; This is considered good practice (at least by Ray Wenderlich), because sending message to a nil var does nothing, while sending message to a deallocated var causes EXC_BAD_ACCESS.
  3. If you get a var from framework methods, then there's this convention:
    • If the method name begins with init or copy, the object returned will have a retain count of 1, and no autorelease pending. In other words, you own that object and have to release it when you’re done.
    • If the method name begins with anything else, the object returned will have a retain count of 1, and an autorelease pending. In other words, you can use the object right now, but if you want to use it later you have to retain the object.
  4. More about property, here's a snapshot of The Elements sample code:

    // Set up the portraitWindow and content view
    UIWindow *localPortraitWindow;
    localPortraitWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.portraitWindow = localPortraitWindow;

    // the localPortraitWindow data is now retained by the application delegate
    // so we can release the local variable
    [localPortraitWindow release];

    Lesson: when you retain a property, you increment its ref count. So you need to release accordingly.
  5. Do this:
    -(NSImage *)getAnImage {

    return [[[NSImage alloc] initWithContentsOfFile:@"/tmp/youownthis.jpg"] autorelease];

    }
    Instead of this:
    -(NSImage *)getAnImage {

    return [[NSImage alloc] initWithContentsOfFile:@"/tmp/youownthis.jpg"];

    }
    The caller will then have to retain the returned object, and subsequently release it.
Now, on the topic of XCode profile -> leaks tool. I made a curious discovery. I build a skeleton table view app, just a navigation controller and a root view controller. Without any string occupying any table cell. And then I ran the profiler with leaks template. I randomly clicked on the table, and I pulled the table up and release it and pull it down and release it and I did it multiple times. And it red bold lines showed up! UIKit framework is leaking memory!

Even the official The Elements sample code from Apple website is leaking memory when tested this way.