Oct 15 2011

Invalid attempt to access ALAssetPrivate past the lifetime of its owning ALAssetsLibrary… WTF?

Category: Objective-c and CocoaDavide Zanotti @ 9:38 am

After the upgrade to iOS 5 and a code refactoring to make use of ARC, I was testing my app and receiving a strange error:

invalid attempt to access ALAssetPrivate past the lifetime of its owning ALAssetsLibrary

this because, I wasn’t aware of an ALAssetsLibrary‘s fact: (quote from Apple’s reference)

The lifetimes of objects you get back from a library instance are tied to the lifetime of the library instance.

This line of text should be displayed inside a warning box to catch developers attention, in fact it is an essential point to understand to create a reliable application that access to device’s media library! The point is that we have to ensure that an instance of ALAssetsLibrary is persisted in memory until we make use of ALAssets retrieved with it. To ensure this, I added a static method to retrieve a shared instance of that class (more or less like a singleton):

1
2
3
4
5
6
7
8
+ (ALAssetsLibrary *)defaultAssetsLibrary {
    static dispatch_once_t pred = 0;
    static ALAssetsLibrary *library = nil;
    dispatch_once(&pred, ^{
        library = [[ALAssetsLibrary alloc] init];
    });
    return library;
}

So, I can refer to it using [MyAssetsManager defaultAssetsLibrary] through my classes and I can use threads (NSOperations an NSOperationQueues) without having to use “trick” like performSelectorOnMainThread.

Tags:


Sep 18 2011

Display full string’s content while debugging in Xcode 4

Category: Objective-c and CocoaDavide Zanotti @ 8:02 am

I spent several time playing with Xcode 4 trying to figure out how to display the full content of a couple of strings during a debugging session. It seems that text displayed in the “variables view” panel are limited to 100 characters, if a string is longer than that size it will be truncated to the first 97 characters and will be added suspension points (…).
To inspect the full content of the string, we have to right click on the desired variable and choose “print description“, at this point xcode will print it to the console (fundamentally is like if we had written an NSLog() in our code at runtime)

Tags: , ,


Jul 24 2011

ALAssetsLibrary’s enumerateGroupsWithTypes:usingBlock:failureBlock: on iOS 4.3.4

Category: Objective-c and CocoaDavide Zanotti @ 5:38 am

I had 20 minutes of panic after installing iOS 4.3.4 on my iPhone 4. In fact, the app which I’m developing has stopped working due to a change in the ALAssetsLibrary api. The call to enumerateGroupsWithTypes:usingBlock:failureBlock:, which I use to retrieve all the media files on the device, did nothing (blocks didn’t get called and no error raised). After a couple of tests:

1
2
3
4
NSLog(@"is selector available? %@",
[library respondsToSelector:@selector(enumerateGroupsWithTypes:usingBlock:failureBlock:)] ? @"y" : @"n");
   
NSLog(@"I'm on the main thread? %@", [NSThread isMainThread] ? @"y" : @"n");

I figured out that the fetching didn’t begins because it was invoked on a background thread (although this did works fine in iOS 4.3.3!).
In fact I’m using an NSOperationQueue with several NSOperations inside and one of them is the one invoking ALAssetsLibrary’s api.
So, to solve the problem I simply created a wrapper to the call using performSelectorOnMainThread:withObject:waitUntilDone:

UPDATE:
After a code refactoring and the upgrade to iOS 5, I finally realized that the problem is actually related to how ALAssetsLibrary works. read here!

Tags: , ,


Jun 25 2011

Handle complex iOS projects with multiple dependencies

Category: Objective-c and CocoaDavide Zanotti @ 5:36 pm

If you are reading this post I’m assuming you are having an hard time and tons of errors from xcode trying to compile third party libraries into your project.
I had my hard time me too! I wrote this little “tutorial” after a whole day of build failures, warnings, tests, google searches, configurations, git clone, svn checkout… and a lot of stress! I hope I will help you saving time and reach your goals, good reading ;)

To include a third party library into our iOS projects there are fundamentally 2 ways. The first, and the simplest, is to include the raw sources (headers and implementation files) into them (using drag and drop or “add files to” from the contextual menu), once done we are ready to go after a simple build. Anyway this is not an ideal solution, because in a scenario in which we have several projects all requiring a particular library, we should copy/paste its sources for each project and moreover we should update library’s sources manually every time it will get updated (in the git/svn repository by the “vendor”)!
The second and the best way, is to manage libraries we will depend on, as sub-projects and linking against them. Here is a brief to-do-list:

What to do

1. Each dependecy should be an xcode static library project, that is an .xcodeproj with a target of type library (“.a“) rather than application (“.app“). In most cases third party libraries are already configured in this way and ready to be imported (step 2)

2. Each project must be added as a sub-project to the main project. You can do this by selecting the main node in the folder tree (that one with the blue icon reporting project’s name) and adding the .xcodeproj file using “add files to...” from the contextual menu (DON’T CHECK “Copy items into destination…” because we are going to create a reference to it not a copy of it!)

3. All the static libraries must be linked to the main project: go to the Target’s “Build Phases” and add the library (.a) in “link binary with libraries

4. The main project should specify all the paths where subprojects headers are located (“header search paths” in Target’s Build Settings).

An absolute path is the best way to speciy this paths, in my case: “/Users/davidezanotti/Documents/X_CODE/CoolLibrary”

IMPORTANT:

If the headers are located under different subfolders (ie: /CoolLibraryPath/headerFolder1/, /CoolLibraryPath/headerFolder2/…)
you can specify just “/projectPath” and check “recursive” to automatically scan each subfolders for .h files, otherwise if they are in a specific folder like “/CoolLibraryPath/src/Classes/” and you don’t want a recursive search, be sure the path string ends with “/”

5. If a project we are referencing to, makes use of particular frameworks (UIKit.framework, SystemConfiguration.framework, Security.framework…), the main project must use these frameworks too. So we have to include them in Target’s “Build Phases” (under “link binary with libraries“)

How to prevent errors:

1. The main project should specify the following flags “-ObjC -all_load” in “Other linker flags” (under Target’s Build Settings). These force the loading of all headers and categories.

2. All subproject should expose their header files as “public”

3. Read CAREFULLY (word by word) instructions that come with downloaded libraries and ensure you did exactly what they say

4. Check CAREFULLY each path typed

5. Pay attention to typo errors

What to do when build fails:

It depends on what type of error(s) has occurred, anyway in 99,9% read again “What to do” and “How to prevent errors” until you will compile. Don’t be fooled by the error message, because it’s often inaccurate and misleading. For example a message like “undefined symbol for architecture i386” may tempt you play with architectures settings when the problem is completely different.
If after several attempts you are still unable to compile the project with its dependencies and you are unable to find the answers on google, try to create a new project from the scratch, add your original classes and assets and then add libraries one at time.

ps. In some case I had to change some import from to “Foo.h” to compile all the projects successfully

Special case: JSON framework

This framework (available here: https://github.com/stig/json-framework/) is used by several libraries, that unfortunately had adopted it by simply copy the sources into the project (first way). The result is that I was unable to compile my project because I get “duplicated symbol” errors because I’m using a couple of them. To solve my issues I cloned the original git repository, added that project into mine and under Target’s “Compile Sources” section of these libraries I removed .h and .m files of the JSON framework. The final result is that I left untouched the downloaded libraries (the files are still where they were), but I can compile without problems because now I don’t have duplicates, all the sources are retrieved from the JSON project once rather than one time for each sources in the library!

Tags: , ,


Jan 08 2011

Bear in mind: you have to cast your numbers to play with them in Objective-C!

Category: Objective-c and CocoaDavide Zanotti @ 10:53 am

Yes, I’m still a newbie when it comes to Objective-C, although I’ve reached a pretty good knowledge level and I’m able to build interfaces, working with files and folders, SQLite api, low level core graphics api and so on, there is always something new to learn that I was ignoring it!
Today I lost more than an hour to understand why a number didn’t get rounded properly. In a method of a class, I was using the ceil() function to round a number returned by a simple division (11/3). The unbelievable thing was that it returned 3 rather than 4 (the right value I was expecting). After several debugging and googling, I realized that I have simply to specify (cast) the numbers I’m going to divide as float to get the right value back from the division!
To specify numbers as floating values, we can use the “.f” syntax or a normal cast, the following code show the scenario:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// wrong way (it will print: "result: 0.000000 - ceiling: 3 - flooring: 12376")
NSLog(@"result: %f - ceiling: %i - flooring: %i",
11/3,
(NSUInteger)ceil(11/3),
(NSUInteger)floor(11/3));

// right 1 (it will print "result: 3.666667 - ceiling: 4 - flooring: 3")
NSLog(@"result: %f - ceiling: %i - flooring: %i",
11.0f/3.0f,
(NSUInteger)ceil(11.0f/3.0f),
(NSUInteger)floor(11.0f/3.0f));

// right 2 (it will print "result: 3.666667 - ceiling: 4 - flooring: 3")
NSLog(@"result: %f - ceiling: %i - flooring: %i",
(float)11/(float)3,
(NSUInteger)ceil((float)11/(float)3),
(NSUInteger)floor((float)11/(float)3));

Tags: ,


« Previous PageNext Page »