2010 02 27Core Image black fringes2
CIImage with [CIImage imageByApplyingTransform:t], you'll see incorrect blending between the image and its background. In this case, a black and grey image on a red background shows darker interpolated pixels than exist in both image and background.
Core Image is by default assuming a black border when blending the image. CIAffineClamp mentions black fringes when using a Gaussian Blur — when blurring, Core Image will blend border pixels with black. Adding CIAffineClamp will extend the image infinitely to blur border pixels with a copy of themselves.
CIAffineClamp — this will remove black borders in CICrystallize, CIPointillize, … and all the blur filters. 2010 02 21Quickest Way to Shell
NSTask to run subprocesses. It can be used to run shell scripts, but all this manual pipe and file handling is a bit cumbersome. Fortunately, for a simple script, we can use the trusty old system and redirect its result to a file.
Here's a quick way to get the path to the last crash report of your app :
// We'll save the result in a temp file NSString* tempFilePath = [NSString stringWithFormat:@"%@/MyApplicationLastCrash.txt", NSTemporaryDirectory()]; // Run the shell script NSString* script = [NSString stringWithFormat: @"ls -1t /Users/mini/Library/Logs/CrashReporter/* | grep /MyApplication | head -1 | tr -d '\n' >%@", tempFilePath]; system([script UTF8String]); // Returns path to last crash report or empty string ( [lastCrash length == 0] ) NSString* lastCrash = [NSString stringWithContentsOfFile:tempFilePath encoding:NSUTF8StringEncoding error:nil];In this case,
ls lists all crash reports from new to old, grep keeps only yours, head and tr extract the result by keeping the first line and removing the trailing newline. Getting the path is now only a matter of reading the temp file. 2010 02 08Who's calling ?
NSThread's callStackSymbols will dump the call stack in an array. What is usually the realm of gdb, callStackSymbols will do at runtime.
Here's how it looks called during awakeFromNib :
0 MyApp -[MyAppDelegate awakeFromNib] + 44 1 CoreFoundation -[NSSet makeObjectsPerformSelector:] + 205 2 AppKit -[NSIBObjectData nibInstantiateWithOwner:topLevelObjects:] + 1445 3 AppKit loadNib + 226 4 AppKit +[NSBundle(NSNibLoading) _loadNibFile:nameTable:withZone:ownerBundle:] + 248 5 AppKit +[NSBundle(NSNibLoading) loadNibNamed:owner:] + 326 6 AppKit NSApplicationMain + 279
When are bindings calling your code ? Walk up the callstack and look for NSBinder :
0 MyApp -[MyAppDelegate setSliderValue:] + 67 1 Foundation _NSSetIntValueAndNotify + 256 2 Foundation -[NSObject(NSKeyValueCoding) setValue:forKey:] + 434 3 Foundation -[NSObject(NSKeyValueCoding) setValue:forKeyPath:] + 240 4 AppKit -[NSBinder _setValue:forKeyPath:ofObject:mode:validateImmediately:raisesForNotApplicableKeys:error:] + 264 5 AppKit -[NSBinder setValue:forBinding:error:] + 266 6 AppKit -[NSValueBinder _applyObjectValue:forBinding:canRecoverFromErrors:handleErrors:typeOfAlert:discardEditingCallback:otherCallback:callbackContextInfo:didRunAlert:] + 197 7 AppKit -[NSValueBinder applyDisplayedValueHandleErrors:typeOfAlert:canRecoverFromErrors:discardEditingCallback:otherCallback:callbackContextInfo:didRunAlert:] + 567 8 AppKit -[NSValueBinder performAction:] + 300 9 AppKit -[_NSBindingAdaptor _objectDidTriggerAction:bindingAdaptor:] + 136 10 AppKit -[NSControl sendAction:to:] + 63 11 AppKit -[NSCell trackMouse:inRect:ofView:untilMouseUp:] + 1715 12 AppKit -[NSSliderCell trackMouse:inRect:ofView:untilMouseUp:] + 1053 13 AppKit -[NSControl mouseDown:] + 624 14 AppKit -[NSWindow sendEvent:] + 5409 15 AppKit -[NSApplication sendEvent:] + 4719 16 AppKit -[NSApplication run] + 474 17 AppKit NSApplicationMain + 364
Writing recursive code and stuck with a method definition that does not specify the recursion depth ? Count how many times the method appears in the call stack.
If you have a dummy NSLog that you want gone but can't find among the legit ones, add this to your precompiled header :
#define NSLog(args...) NSLog(args), NSLog(@"%@", [NSThread callStackSymbols])It will call the original
NSLog and pinpoint the offending function. 2009 09 218 ways to use Blocks in Snow Leopard2
1 Custom sorting To sort an NSArray in a custom way, you need to write a new method and pass its selector to the sorting function. With blocks, just sort in-place :
id sortedArray = [myArray sortedArrayUsingComparator:
^(id a, id b)
{
if (a.someValue > b.someValue && a.otherValue > b.otherValue) return NSOrderedAscending;
... some more sorting code
}];
2 Enumeration Looking for an object in an array can be done with a block enumerator. Set stop to YES to stop enumeration.
[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
NSLog(@"obj %@", obj);
if (...terminatingCondition...) *stop = YES;
}];
This is the least interesting use of blocks. The for .. in is much more readable :
for (id obj in myArray)
{
if (...terminatingCondition...) break;
}
The one advantage enumerateObjectsUsingBlock: has is the current index. enumerateObjectsWithOptions:usingBlock: lets you go backwards.
3 Just-this-scope functions You need to call a piece of code multiple times but don't feel like adding a new method to your class just to accomplish this one task. For instance, protocol_copyMethodDescriptionList(protocol, isRequired, isInstance, count) needs multiple calls to enumerate all protocol methods. Instead of creating a new method, use a block :
- (void)enumerateProtocolMethods:(Protocol*)p
{
// Custom block, used only in this method
void (^enumerate)(BOOL, BOOL) = ^(BOOL isRequired, BOOL isInstance) {
unsigned int descriptionCount;
struct objc_method_description* methodDescriptions =
protocol_copyMethodDescriptionList(p, isRequired, isInstance, &descriptionCount);
for (int i=0; i<descriptionCount; i++)
{
struct objc_method_description d = methodDescriptions[i];
NSLog(@"Protocol method %@ isRequired=%d isInstance=%d",
NSStringFromSelector(d.name), isRequired, isInstance);
}
if (methodDescriptions) free(methodDescriptions);
};
// Call our block multiple times with different arguments
// to enumerate all class, instance, required and non-required methods
enumerate(YES, YES);
enumerate(YES, NO);
enumerate(NO, YES);
enumerate(NO, NO);
}
4 Common cleanup I trust we've all done this :
- (id)veryFragileDaisyChain
{
... init 1
if (condition1) {
... init 2
if (condition2) {
... init 3
if (condition3) {
... init 4
if (condition4) {
// At last ! Our shiny new object !
}
... init 3 cleanup
}
... init 2 cleanup
}
... init 1 cleanup
}
}
How about a block ?
- (id)veryFragileDaisyChain
{
// Let's declare everything in advance
__block id obj1 = nil, obj2 = nil, obj3 = nil;
// Cleanup block
void(^cleanup)() = ^{
if (obj3) ... cleanup
if (obj2) ... cleanup
if (obj1) ... cleanup
};
// Onward.
... init code 1
if (!condition1) return cleanup(), nil;
... init code 2
if (!condition2) return cleanup(), nil;
... init code 3
if (!condition3) return cleanup(), nil;
... init code 4
if (!condition4) return cleanup(), nil;
// Done.
return shinyNewObject;
}
5 Functional style Nicolas seriot wrote about functional code in ObjC before Snow Leopard was out, hinting at using blocks to implement functional style code in ObjC. Strangely enough, Snow Leopard didn't implement these. NSArray does have a indexesOfObjectsPassingTest: method, but it returns a NSIndexSet instead of an array ?! With blocks, we can implement a filter method that will create a new array collecting objects matching our block condition.
@implementation NSArray(FunctionalStyle)
- (NSArray*)filter:(BOOL(^)(id elt))filterBlock
{
// Create a new array
id filteredArray = [NSMutableArray array];
// Collect elements matching the block condition
for (id elt in self)
if (filterBlock(elt)) [filteredArray addObject:elt];
return filteredArray;
}
@end
This removes the need to loop over array element ourselves when we only want to extract elements. Just supply a condition and voilà ! A new array.
// Return a new array containing elements greater than 4
id filteredArray = [myArray filter:^(id elt) {
return [elt intValue] > 4;
}];
6 List comprehensions are a shorthand way of creating arrays.
// This shorthand Javascript var s = [2*i for (i in 100) if (i*i>3)] // is equivalent to var newArray = [] for (var i=0; i<100; i++) if (i*i>3) newArray.push(2*i)
We can implement the same with blocks, just a little more verbosely :
@implementation NSArray(ListComprehensions)
// Create a new array with a block applied to each index to create a new element
+ (NSArray*)arrayWithBlock:(id(^)(int index))block range:(NSRange)range
{
id array = [NSMutableArray array];
for (int i=range.location; i<range.location+range.length; i++)
[array addObject:block(i)];
return array;
}
// The same with a condition
+ (NSArray*)arrayWithBlock:(id(^)(int index))block range:(NSRange)range if:(BOOL(^)(int index))blockTest
{
id array = [NSMutableArray array];
for (int i=range.location; i<range.location+range.length; i++)
if (blockTest(i)) [array addObject:block(i)];
return array;
}
@end
Then call with a block and a range, and an optional condition :
// Just block and range
id newArray = [NSArray
arrayWithBlock:^(int i) { return [NSNumber numberWithInt:i*2]; }
range:NSMakeRange(5, 10)];
// Optional condition : keep only multiples of 3
id newArray = [NSArray
arrayWithBlock:^(int i) { return [NSNumber numberWithInt:i*2]; }
range:NSMakeRange(5, 10)
if:^(int i) { return i%3 == 0; } ];
7 Async processing This is a simple piece of Grand Central Dispatch : enqueue an asynchronous job. The function will return immediately while the block performs in the background in another thread. Use performSelectorOnMainThread: to update your GUI afterwards.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
// Some looong computation in here ...
// [NSThread isMainThread] tells us that calling [self taskDone] is done with the dispatch thread
// Use this to call our end of job notification on the main thread
[self performSelectorOnMainThread:@selector(taskDone) withObject:nil waitUntilDone:NO];
});
8 Async monitoring Another piece of GCD, lifted from the Concurrency Programming Guide.
You can use GCD to monitor asynchronously resources. In this example, a dispatch source is set to watch when a file is renamed. One block handles notification, another handles common cleanup.
// Create a dispatch source and start watching it
- (dispatch_source_t)watchFileRename:(NSString*)fileName
{
int fd = open([fileName UTF8String], O_EVTONLY);
if (fd == -1) return NULL;
[fileName retain];
// Cleanup block
void(^cleanup)() = ^{
close(fd);
[fileName release];
};
// Create source
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE,
fd, DISPATCH_VNODE_RENAME,
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
if (!source) return cleanup(), NULL;
// Dispatch source handler
dispatch_source_set_event_handler(source, ^{
// *** Async event handler ***
// Called when the file was renamed.
});
// Dispatch source destructor
dispatch_source_set_cancel_handler(source, ^{ cleanup(); });
// Start watching
dispatch_resume(source);
return source;
}
// Release a dispatch source
- (void)releaseDispatchSource:(dispatch_source_t)source
{
if (!source) return;
dispatch_source_cancel(source);
dispatch_release(source);
}
// Start watching file rename. Call releaseDispatchSource when done.
dispatch_source_t source = [self watchFileRename:@"/FileToWatch"];
There are even more uses of blocks, as Grand Central Dispatch uses them extensively to perform concurrent tasks. I encourage you to read the Concurrency Programming Guide. 2009 08 18Bracket Mess2
// Allocate a new object var a = [MyObject new]
Mixing both styles looks buggy but is actually correct :
// Make a new array containing a new object var array = [[MyObject new]] // Make a new array containing an alloc'ed object, get that object, call init on it, save it in its own array var obj = [[[MyObject alloc]][0] init] // (This gets translated as var obj = [MyObject.alloc][0].init
After your object is initialized, you can use Javascript-like access for hashes and dictionaries :
// Allocate a new object as the only element of an array immediate, init it, // then gets it 'keys' properties, then the 'name' property of 'keys' var obj = [[[[MyObject alloc]][0] init] keys]['name']
JSCocoa will translate Javascript array and hash immediates to NSArray and NSDictionary instances, so they can also be used as parameter values ...
var o = [[MyObject alloc] initWithOptions: { orientation : 'vertical', count : 5 }]
or to retrieve an instance to call on to :
// Call a method from an object stored in a js array
var array = [MyObject new, MyObject new]
[array[0] doSomething]
// Call a method from an object stored in a js hash
var hash = { 'value' : [MyObject new] }
[hash['value'] doSomethingElse]
I'm only using these as stress test, no code needs that many brackets ! 2009 08 12Taming JavascriptCore within and without WebView1
On a Mac ? Check out JSCocoa, which bridges Cocoa and Javascript. Its latest version let you manipulate WebViews straight from your JSCocoa context.
Onward !
Starting up
All JavascriptCore code exists in a JavascriptCore context.
// Init JSContextRef ctx = JSGlobalContextCreate(NULL); // Do some work ... // Release JSGlobalContextRelease(ctx);
The simplest call that might possibly work
To create an array, a hash, a new Date object, to test if objects match a condition … JSObjectCallAsFunction is the function to use. It calls a Javascript function with as many arguments as you supply. It can also call anonymous functions. Anonymous functions don't have names, won't clog up your namespace, and will disappear as soon as your destroy all references to them. To get a result, create an anonymous function containing your code, call it, and handle the result. JavascriptCore will cleanup behind you.
// Create a new array
JSStringRef scriptJS = JSStringCreateWithUTF8CString("return new Array");
// Create an anonymous function and call it
JSObjectRef fn = JSObjectMakeFunction(ctx, NULL, 0, NULL, scriptJS, NULL, 1, NULL);
JSValueRef result = JSObjectCallAsFunction(ctx, fn, NULL, 0, NULL, NULL);
// Release script
JSStringRelease(scriptJS);
result will then contain a new array. Any object creation is better handled through anonymous functions, instead of writing the C code for getting an object reference and calling its constructor.
// Create a hash
JSStringRef scriptJS = JSStringCreateWithUTF8CString("return { hello : 'world', value : 123 }");
// Create a new Date object
JSStringRef scriptJS = JSStringCreateWithUTF8CString("return new Date");
// Even create another anonymous function !
JSStringRef scriptJS = JSStringCreateWithUTF8CString("return function (a, b) { return a+b }");
Arguments
To test if objects match a condition, to build a new object from existing ones, … put your arguments in a C array.
// One argument only : use its address as a one-value C array
// (Test if argument is an array)
JSStringRef scriptJS = JSStringCreateWithUTF8CString("return arguments[0].constructor == Array.prototype.constructor");
JSValueRef result = JSObjectCallAsFunction(ctx, fn, NULL, 1, (JSValueRef*)&jsObject, NULL);
// Multiple arguments
JSValueRef args[] = { arg1, arg2, arg3 };
JSValueRef result = JSObjectCallAsFunction(ctx, fn, NULL, 3, args, NULL);
Your own objects
JavascriptCore lets you define callbacks for when your object is created, destroyed, queried for a property, called as a function or a constructor, and when something wants to set a new property on it.
// Create a class definition and register it
JSClassDefinition myJavascriptClass = kJSClassDefinitionEmpty;
myJavascriptClass.getProperty = myClassGetProperty;
myClass = JSClassCreate(&myJavascriptClass);
static JSValueRef myClassGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyNameJS, JSValueRef* exception)
{
// Will be called when JavascriptCore requests propertyNameJS on your object
}
Instancing your own objects
Use JSObjectMake and pass your class. Return this object to Javascript and myClassGetProperty will be called when queried for a property.
// Create a Javascript object with a particular class JSObjectRef o = JSObjectMake(ctx, jsCocoaObjectClass, NULL);
Your own data
An object can hold a private pointer. Once again, use JSObjectMake and specify the class and a pointer to your data. You may want to add a finalize callback to be notified when JavascriptCore is destroying the object, as it will be time for cleanup. Here, jsCocoaObjectClass'sfinalize callback would release the ObjC object held as private data.
// Create a Javascript object with a particular class and holding private data MyObject* privateData = [MyObject new]; JSObjectRef o = JSObjectMake(ctx, jsCocoaObjectClass, privateData);
You can also use JSObjectGetPrivate and JSObjectSetPrivate.
Creating Javascript objects
Either call JSObjectCallAsFunction to create objects or use JSValueMake* functions.
// Return a number
return JSValueMakeNumber(ctx, 1.23);
// Return a number
return JSValueMakeNull;
// Return a string
JSStringRef string = JSStringCreateWithUTF8CString("Hello world");
JSValueRef result = JSValueMakeString(ctx, string);
JSStringRelease(string);
return result;
Converting from type to type
Javascript uses a these types : string, number, boolean, null, undefined, object. Convert between these with JSValueTo*. These functions are equal to calling the equivalent type constructor, like var boolResult = Boolean(value) in Javascript.
// Convert to Boolean JSValueRef boolResult = JSValueToBoolean(ctx, value);
Get a native string out of any JavascriptCore object
JSValueToStringCopy will call toString on an object and return a Javascript string that you can then convert to an NSString.
JSStringRef resultStringJS = JSValueToStringCopy(ctx, value, NULL); NSString* resultString = (NSString*)JSStringCopyCFString(kCFAllocatorDefault, resultStringJS); NSLog(@"Javascript value=%@", resultString); // Release string when done JSStringRelease(resultStringJS); // And autorelease our NSString [NSMakeCollectable(resultString) autorelease];
JSStringGetMaximumUTF8CStringSize, JSStringGetUTF8CString will return a UTF8 string.
Garbage collection
JavascriptCore releases objects you've created, so you almost have not to worry about GC. Any result coming from calling JSValueMakeNumber, JSObjectMake is watched by GC and will be collected. Exceptions :
-
JSStringCreateWithUTF8CString,JSStringCreateWithCFStringneedsJSStringRelease -
JSObjectCopyPropertyNames(An array of an object's property names) needsJSPropertyNameArrayRelease -
JSClassCreateneedsJSClassRelease - And the Javascript context itself, created with
JSGlobalContextCreateneedsJSGlobalContextRelease
Protecting objects from Garbage Collection
To prevent JavascriptCore from collecting a Javascript object, call JSValueProtect. When done, call JSValueUnprotect. This works for JSValueRef and JSObjectRef.
This is useful to keep stuff around. In JSCocoa, some ObjC objects can hold a Javascript object, thereby containing an infinite number of variables without needing to declare a method for each. As JavascriptCore has no way of knowing this, JSValueProtect marks that object as protected.
Responding to callbacks
Now that you know how to create data, call functions, convert … you can write the callbacks to your objects.
static JSValueRef myClassGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyNameJS, JSValueRef* exception)
{
// Get the property name as a NSString
NSString* propertyName = (NSString*)JSStringCopyCFString(kCFAllocatorDefault, propertyNameJS);
[NSMakeCollectable(propertyName) autorelease];
// From there you can return values, functions, objects, or throw an exception
if ([propertyName isEqualToString:@"myNumber"]) return JSValueMakeNumber(ctx, 1.23);
if ([propertyName isEqualToString:@"hello"])
{
JSStringRef string = JSStringCreateWithUTF8CString("world");
JSValueRef result = JSValueMakeString(ctx, string);
JSStringRelease(string);
return result;
}
// Throw an exception
if ([propertyName isEqualToString:@"notYet"])
{
JSStringRef string = JSStringCreateWithUTF8CString("Data not ready");
JSValueRef exceptionString = JSValueMakeString(ctx, string);
JSStringRelease(string);
// Converting the result to an object will let JavascriptCore add source URL and line number to the exception,
// instead of just returning a raw string
*exception = JSValueToObject(ctx, exceptionString, NULL);
return NULL;
}
// JavascriptCore might crash if you return NULL in some functions, so always return a Javascript object
// Here, we return undefined
return JSValueMakeUndefined(ctx);
}
Global scope, like Safari's window
In a Web window, functions and attributes like eval, setInterval, document are global variables. They're part of window, which is the global object : window.document and document retrieve the same object . To get your own global object, use a custom class when creating your context.
// JavascriptCore will call myGlobalClass's getProperty when not finding a variable JSContextRef ctx = JSGlobalContextCreate(myGlobalClass);
Within WebView
WebView holds its own JavascriptCore context and lets you access it with globalContext on a frame. You can then add your custom objects and call custom code through this context.
// Get the window context JSContextRef webViewCtx = [[WebView mainFrame] globalContext]; // Call a js function JSValueRef result = JSObjectCallAsFunction(webViewCtx, ...)
If you want to hold on to objects, you might want to call JSGlobalContextRetain as the WebView could be destroyed before your objects. Call JSGlobalContextRelease when done.
2009 04 15Debugging with Activity Monitor
To draw a custom header in a table view, derive from NSTableHeaderCell and write your drawing code in highlight:withFrame:inView:. This works fine until you disable column selection : highlight:withFrame:inView: is never called, although the header cell obviously looks highlighted. To understand why, we can use … Activity Monitor ! As strange as it sounds, double click your process listing to get a sample button. Instead of launching Shark and getting a trace from everything, we can sample for one moment while clicking around to highlight those table header cells. Behold :
// Column selection enabled 1 -[NSTableHeaderView drawRect:] 1 -[NSTableHeaderView _drawColumnHeaderWithIndexes:] 1 -[NSTableHeaderView _drawHeaderOfColumn:] 1 -[NSTableHeaderCell highlight:withFrame:inView:] 1 -[NSTableHeaderCell _drawThemeContents:highlighted:inView:] 1 -[NSTableHeaderCell drawInteriorWithFrame:inView:] // Column selection disabled 3 -[NSTableHeaderView drawRect:] 3 -[NSTableHeaderView _drawColumnHeaderWithIndexes:] 3 -[NSTableHeaderView _drawHeaderOfColumn:] 3 -[NSTableHeaderView _drawHeaderCell:withFrame:withStateFromColumn:] 3 -[NSTableHeaderCell drawWithFrame:inView:] 3 -[NSTableHeaderCell _drawThemeContents:highlighted:inView:] 3 -[NSTableHeaderCell _drawBezelWithFrame:highlighted:inView:]
Et voilà ! When column selection is disabled, highlight:withFrame:inView: is never called. _drawBezelWithFrame:highlighted:inView: is called instead. Hey, there's even some code using that very method ! Using it to draw the highlighted cell does work.