Creating window from scratch [duplicate] - objective-c

I'm creating an application in pure C on Mac OSX.
What I want is to create window in witch my app will be stored.
Preferably I want it to be pure C solution, but if I have to use objective-c class to init window and then send context to my C code then it will be fine.
I'm not using xcode, only simple text editor in with I tried to import cocoa but it just generated a lot of errors.
So in summary my question is:
How in simple pure C generate code that will display osx window?

I did a translation of the accepted answer to Pure C:
// based on https://stackoverflow.com/a/30269562
// Minimal Pure C code to create a window in Cocoa
// $ clang minimal.c -framework Cocoa -o minimal.app
#include <objc/runtime.h>
#include <objc/message.h>
#include <Carbon/Carbon.h>
#define cls objc_getClass
#define sel sel_getUid
#define msg ((id (*)(id, SEL, ...))objc_msgSend)
#define cls_msg ((id (*)(Class, SEL, ...))objc_msgSend)
// poor man's bindings!
typedef enum NSApplicationActivationPolicy {
NSApplicationActivationPolicyRegular = 0,
NSApplicationActivationPolicyAccessory = 1,
NSApplicationActivationPolicyERROR = 2,
} NSApplicationActivationPolicy;
typedef enum NSWindowStyleMask {
NSWindowStyleMaskBorderless = 0,
NSWindowStyleMaskTitled = 1 << 0,
NSWindowStyleMaskClosable = 1 << 1,
NSWindowStyleMaskMiniaturizable = 1 << 2,
NSWindowStyleMaskResizable = 1 << 3,
} NSWindowStyleMask;
typedef enum NSBackingStoreType {
NSBackingStoreBuffered = 2,
} NSBackingStoreType;
int main(int argc, char *argv[])
{
// id app = [NSApplication sharedApplication];
id app = cls_msg(cls("NSApplication"), sel("sharedApplication"));
// [app setActivationPolicy:NSApplicationActivationPolicyRegular];
msg(app, sel("setActivationPolicy:"), NSApplicationActivationPolicyRegular);
struct CGRect frameRect = {0, 0, 600, 500};
// id window = [[NSWindow alloc] initWithContentRect:frameRect styleMask:NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskResizable backing:NSBackingStoreBuffered defer:NO];
id window = msg(cls_msg(cls("NSWindow"), sel("alloc")),
sel("initWithContentRect:styleMask:backing:defer:"),
frameRect,
NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskResizable,
NSBackingStoreBuffered,
false);
msg(window, sel("setTitle:"), cls_msg(cls("NSString"), sel("stringWithUTF8String:"), "Pure C App"));
// [window makeKeyAndOrderFront:nil];
msg(window, sel("makeKeyAndOrderFront:"), nil);
// [app activateIgnoringOtherApps:YES];
msg(app, sel("activateIgnoringOtherApps:"), true);
msg(app, sel("run"));
}

You can use Objective-C runtime API example (iOS) Creating an iOS app in pure C
Alternative the same code in obj-c :
echo '#import <Cocoa/Cocoa.h>
int main ()
{
#autoreleasepool{
[NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
id applicationName = [[NSProcessInfo processInfo] processName];
id window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 120, 120)
styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO];
[window cascadeTopLeftFromPoint:NSMakePoint(20,20)];
[window setTitle: applicationName];
[window makeKeyAndOrderFront:nil];
[NSApp activateIgnoringOtherApps:YES];
[NSApp run];
}
return 0;
}' | gcc -fobjc-arc -framework Cocoa -x objective-c -o MicroApp - ; ./MicroApp
This will run Cocoa app with 1 window. Like on screenshot below
You can actually add menu using NSMenu
id applicationMenuBar = [NSMenu new];
id appMenuItem = [NSMenuItem new];
[applicationMenuBar addItem:appMenuItem];
[NSApp setMainMenu: applicationMenuBar];

Can you do this? Yes and no (you can do anything if you're persistent enough). Yes you can, but no you shouldn't. Regardless, this can be done for the incredibly persistent among you. Since coding up an example will take awhile, I found a generous soul on the net who already did it. Look at this repository on GitHub for the full code and explanations. Here are some snippets:
cmacs_simple_msgSend((id)objc_getClass("NSApplication"), sel_getUid("sharedApplication"));
if (NSApp == NULL) {
fprintf(stderr,"Failed to initialized NSApplication... terminating...\n");
return;
}
id appDelObj = cmacs_simple_msgSend((id)objc_getClass("AppDelegate"), sel_getUid("alloc"));
appDelObj = cmacs_simple_msgSend(appDelObj, sel_getUid("init"));
cmacs_void_msgSend1(NSApp, sel_getUid("setDelegate:"), appDelObj);
cmacs_void_msgSend(NSApp, sel_getUid("run"));
As you'll notice, this code uses the Objective-C runtime API to create a faux AppDelegate. And creating the window is an involved process:
self->window = cmacs_simple_msgSend((id)objc_getClass("NSWindow"), sel_getUid("alloc"));
/// Create an instance of the window.
self->window = cmacs_window_init_msgSend(self->window, sel_getUid("initWithContentRect:styleMask:backing:defer:"), (CMRect){0,0,1024,460}, (NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask), 0, false);
/// Create an instance of our view class.
///
/// Relies on the view having declared a constructor that allocates a class pair for it.
id view = cmacs_rect_msgSend1(cmacs_simple_msgSend((id)objc_getClass("View"), sel_getUid("alloc")), sel_getUid("initWithFrame:"), (CMRect){ 0, 0, 320, 480 });
// here we simply add the view to the window.
cmacs_void_msgSend1(self->window, sel_getUid("setContentView:"), view);
cmacs_simple_msgSend(self->window, sel_getUid("becomeFirstResponder"));
// Shows our window in the bottom-left hand corner of the screen.
cmacs_void_msgSend1(self->window, sel_getUid("makeKeyAndOrderFront:"), self);
return YES;
So, yes. You can write a Cocoa app in pure C. But I wouldn't recommend it. 90% of that code can be replaced by an xib file, and doing it this way really restricts your app because more advanced features of the Apple development stack really on Objective-C features. While it's technically possible to do everything this way, you're making it much harder than it ought to be.

I remember seeing this question about a year ago, back when I so desperately wished I could open up a d*** window, googling for days and only finding the type of answers you see above this post.
I was reading up on the operating system the Mac is built on - Berkley Software Distribution. http://codex.cs.yale.edu/avi/os-book/OS9/appendices-dir/a.pdf Where on page 17 the phrase "...X Windowing System developed at MIT" hit me and I remembered how I couldn't open up a window and how pissed I was about that, and I thought maybe this was finally the solution!
I googled "BSD X Window Programming" and stumbled my way into finally getting a window open in pure C.
I just discovered it so I'm not a master yet but look at this link https://en.wikibooks.org/wiki/X_Window_Programming/Xlib and go to the example, make sure to follow the comments at the top for how to compile with the X11 library (you can ignore the -Wall and -O commands as long as you have the -lX11).
If you can't compile, if it can't find the header files, you'll need to help it find the header files.
There might be a couple different places that the X11 includes could be on your system. More than likely you'll find it in /opt/X11/include which will have all the definitions of the headers you'll need.
You could include the full path in your C programs such as:
#include "/opt/X11/include/X11/Xlib.h"
But we want it to look like this #include <X11/Xlib.h>
So you could add this switch to GCC when you compile -I /opt/X11/include
Or go to your .profile or .bashrc or .bash_profile in your home directory and add:
export C_INCLUDE_PATH="$C_INCLUDE_PATH:/opt/X11/include"
/*
Simple Xlib application drawing a box in a window.
*/
From the wiki:
#include<X11/Xlib.h>
#include<stdio.h>
#include<stdlib.h> // prevents error for exit on line 18 when compiling with gcc
int main() {
Display *d;
int s;
Window w;
XEvent e;
/* open connection with the server */
d=XOpenDisplay(NULL);
if(d==NULL) {
printf("Cannot open display\n");
exit(1);
}
s=DefaultScreen(d);
/* create window */
w=XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 100, 100, 1,
BlackPixel(d, s), WhitePixel(d, s));
// Process Window Close Event through event handler so XNextEvent does Not fail
Atom delWindow = XInternAtom( d, "WM_DELETE_WINDOW", 0 );
XSetWMProtocols(d , w, &delWindow, 1);
/* select kind of events we are interested in */
XSelectInput(d, w, ExposureMask | KeyPressMask);
/* map (show) the window */
XMapWindow(d, w);
/* event loop */
while(1) {
XNextEvent(d, &e);
/* draw or redraw the window */
if(e.type==Expose) {
XFillRectangle(d, w, DefaultGC(d, s), 20, 20, 10, 10);
}
/* exit on key press */
if(e.type==KeyPress)
break;
// Handle Windows Close Event
if(e.type==ClientMessage)
break;
}
/* destroy our window */
XDestroyWindow(d, w);
/* close connection to server */
XCloseDisplay(d);
return 0;
}
Compile:
gcc -O2 -Wall -o test test.c -L /usr/X11R6/lib -lX11 -lm

Unfortunately the top rated answer doesn't work on new Apple Silicon powered machines due to an ABI mismatch. Basically on ARM64 you can't use the objc_msgSend declaration with variable arguments, you must specify the correct argument types for each call. Here is the version that runs on Apple Silicon:
// based on https://stackoverflow.com/a/59596600/834108
// Minimal Pure C code to create a window in Cocoa
// Adapted to work on ARM64
// $ clang minimal.c -framework Cocoa -o minimal.app
#include <objc/runtime.h>
#include <objc/message.h>
#include <Carbon/Carbon.h>
#define cls objc_getClass
#define sel sel_getUid
#define msg ((id (*)(id, SEL))objc_msgSend)
#define msg_int ((id (*)(id, SEL, int))objc_msgSend)
#define msg_id ((id (*)(id, SEL, id))objc_msgSend)
#define msg_ptr ((id (*)(id, SEL, void*))objc_msgSend)
#define msg_cls ((id (*)(Class, SEL))objc_msgSend)
#define msg_cls_chr ((id (*)(Class, SEL, char*))objc_msgSend)
// poor man's bindings!
typedef enum NSApplicationActivationPolicy {
NSApplicationActivationPolicyRegular = 0,
NSApplicationActivationPolicyAccessory = 1,
NSApplicationActivationPolicyERROR = 2,
} NSApplicationActivationPolicy;
typedef enum NSWindowStyleMask {
NSWindowStyleMaskBorderless = 0,
NSWindowStyleMaskTitled = 1 << 0,
NSWindowStyleMaskClosable = 1 << 1,
NSWindowStyleMaskMiniaturizable = 1 << 2,
NSWindowStyleMaskResizable = 1 << 3,
} NSWindowStyleMask;
typedef enum NSBackingStoreType {
NSBackingStoreBuffered = 2,
} NSBackingStoreType;
int main(int argc, char *argv[])
{
// id app = [NSApplication sharedApplication];
id app = msg_cls(cls("NSApplication"), sel("sharedApplication"));
// [app setActivationPolicy:NSApplicationActivationPolicyRegular];
msg_int(app, sel("setActivationPolicy:"), NSApplicationActivationPolicyRegular);
struct CGRect frameRect = {0, 0, 600, 500};
// id window = [[NSWindow alloc] initWithContentRect:frameRect styleMask:NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskResizable backing:NSBackingStoreBuffered defer:NO];
id window = ((id (*)(id, SEL, struct CGRect, int, int, int))objc_msgSend)(
msg_cls(cls("NSWindow"), sel("alloc")),
sel("initWithContentRect:styleMask:backing:defer:"),
frameRect,
NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskResizable,
NSBackingStoreBuffered,
false
);
msg_id(window, sel("setTitle:"), msg_cls_chr(cls("NSString"), sel("stringWithUTF8String:"), "Pure C App"));
// [window makeKeyAndOrderFront:nil];
msg_ptr(window, sel("makeKeyAndOrderFront:"), nil);
// [app activateIgnoringOtherApps:YES];
msg_int(app, sel("activateIgnoringOtherApps:"), true);
msg(app, sel("run"));
}

Pure C cross-platform example: (Windows/macOS/Linux)
https://nappgui.com/en/demo/products.html
About macOS portability in pure C (updated to BigSur and M1 support):
https://nappgui.com/en/start/win_mac_linux.html#h2

I'm creating an application in pure C on Mac OSX. What I want is to create window in which my app will be stored.
Are you looking for a TTY window?
If so does your application need to create the window?
If not then you can simply write your pure C program and execute it from within Terminal - a TTY environment for "pure C".
If you want a double-clickable app you can write an AppleScript which will open Terminal and run your C. Something like:
tell application "Terminal"
do script "ex /tmp/test; exit"
end tell
This opens a Terminal window showing "ex" and when that quits will terminate the shell process (so no further commands can be typed), but it will not close Terminal itself - for that you will have to work harder.
If you do want you application to create the window itself you either need to write your own simple TTY window, you might find some classes you can use, or you might be able to borrow code from an open source terminal app such as iterm.
HTH

Related

Using objc_msgSendSuper_stret will raise `EXC_BAD_ACCESS(code=EXC_i386_GPFLT)`

I'm using objc_msgSendSuper and objc_msgSendSuper_stret to invoke the superclass's implementation of its superclass.
The objc_msgSendSuper works well as following:
struct objc_super supersSuper;
supersSuper.receiver = self;
supersSuper.super_class = object_getClass(class_getSuperclass(class_getSuperclass(self)));
return objc_msgSendSuper(&supersSuper, _cmd);
But using objc_msgSendSuper_stret will raise a runtime error EXC_BAD_ACCESS(code=EXC_i386_GPFLT):
// In Tier3 class
+ (CGPoint) point{
struct objc_super supersSuper;
supersSuper.receiver = self;
supersSuper.super_class = object_getClass(class_getSuperclass(class_getSuperclass(self)));
// raise EXC_BAD_ACCESS(code=EXC_i386_GPFLT) when invoking objc_msgSendSuper_stret
return ((CGPoint(*)(struct objc_super *, SEL))objc_msgSendSuper_stret)(&supersSuper, _cmd);
}
The selector did exist in the target class:
[Tier1 point]; // return {10, 10}
I see lots of objc_msgSend, objc_msgSendSuper and objc_msgSend_stret examples, but it's rarely to find an example using objc_msgSendSuper_stret.
Update
I found the old API of objc_msgSend_stret is the following:
void objc_msgSend_stret(void * stretAddr, id theReceiver, SEL theSelector, ...)
but current API is:
void objc_msgSend_stret(id self, SEL op, ...)
I think Apple did change the function signature of objc_msgSend_stret. Doesn't it?
Update-2
I did try the following:
CGPoint retVal = CGPointMake(0, 0);
// Cast to old API signature.
((void(*)(CGPoint *, id, SEL))objc_msgSend_stret)(&retVal, self, #selector(point));
return retVal;
This casting will make the function objc_msgSend_stret call succeed. The target method did been invoked, and the target method returns {10, 10}. But the return value which we got is not correct, it was still {0, 0}.
BTW, I find the code can't work under 32-bit architecture (still raise EXC_BAD_ACCESS(code=EXC_i386_GPFLT)). It only works under 64-bit architecture.
Update-3
Class declaration:
#import <objc/runtime.h>
#import <objc/message.h>
#interface TestClass : NSObject
+ (CGRect)rect;
+ (CGPoint)point;
#end
#implementation TestClass
+ (CGRect)rect;{
NSLog(#"+[TestClass rect] is invoked");
return CGRectMake(3, 3, 3, 3);
}
+ (CGPoint)point;{
NSLog(#"+[TestClass point] is invoked");
return CGPointMake(10, 10);
}
+ (CGRect)rectRuntimeTest;{
CGRect retVal = CGRectMake(0, 0, 0, 0);
((void(*)(CGRect *, id, SEL))objc_msgSend_stret)(&retVal, self, #selector(rect));
return retVal;
}
+ (CGPoint)pointRuntimeTest{
CGPoint retVal = CGPointMake(0, 0);
((void(*)(CGPoint *, id, SEL))objc_msgSend_stret)(&retVal, self, #selector(point));
return retVal;
}
#end
In application:didFinishLaunchingWithOptions:, test the following:
CGRect retRect = [TestClass rectRuntimeTest];
NSLog(#"retVal: %#", NSStringFromCGRect(retRect));
CGPoint retPoint = [TestClass pointRuntimeTest];
NSLog(#"retPoint: %#", NSStringFromCGPoint(retPoint));
The output (under 64-bit architecture iOS Simulator):
> +[TestClass rect] is invoked
> retVal: {{3, 3}, {3, 3}}
> +[TestClass point] is invoked
> retPoint: {0, 0} // Should be {10, 10} which is returned from +[TestClass point]
The output (under 32-bit architecture iOS Simulator):
`EXC_BAD_ACCESS (code=2, address=0xbfffca78)`
From this question, it appears that CGPoint is a "simple" return value, and so you should use objc_msgSendSuper in this case. When returning CGRect, you apparently should use objc_msgSendSuper_stret.
Regarding the strange signature of the _stret variants, don't let the private signatures bother you, relying on the public signatures is correct. This is showcased in detail here.
Other than that, you should always cast objc_msg*Send* to the non-variadic (i.e., with all arguments well-defined) form, and call that (you do it in some places, but you should in all places).
I might be wrong, but I did some research and the so called "simple" return value is "simple" when the structure is returned in the register or not.
The POD (Plain Old Data) structs will be returned through registers if they fit some standard criteria according to these ABIs:
IA-21 ABI (a.k.a x86, https://developer.apple.com/library/prerelease/content/documentation/DeveloperTools/Conceptual/LowLevelABI/130-IA-32_Function_Calling_Conventions/IA32.html#//apple_ref/doc/uid/TP40002492-SW5) - structs of size 1 or 2 bytes are returned in eax, 4 or 8 bytes are returned in eax and edx.
AMD64 ABI (a.k.a. x86_64, https://people.freebsd.org/~obrien/amd64-elf-abi.pdf#14, section 3.2.3) - if a struct fits into two 8-bytes then it's returned through registers rax and rdx.
ARM Procedure Call Standard (http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042f/IHI0042F_aapcs.pdf, section 5.4) - up to 4 bytes are returned through r0.
ARM64 Procedure Call Standard (http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf, section 5.5) - it looks like the same rules apply for returning value as passing arguments - that means that composite types up to 8 registers (8 * 8 = 64 bytes max) are passed back into registers r0-r8.
So to sum everything up we have to use objc_msgSend_stret function when:
x86 and sizeof(struct) == 1 | 2 | 4 | 8
x86_64 and sizeof(struct) > 16
armv7 and sizeof(struct) > 4
arm64 and sizeof(struct) > 64
Else just use objc_msgSend
I'm not 100% sure about arm64, because the standard is wayyy too abstract :)

#define or #if statement error

I have read many definitions of # if and # define statement.
I tried to use the way I read, but only with the error "Invalid token at start of a preprocessor expression" on the line that defines it as a comment below:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#define is_ipad (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
#define is_ipad_hd ( (is_ipad == 1 && [UIScreen mainScreen].bounds.size.width > 1024 ) ? YES : NO)
#define is_iphone_hd ([UIScreen mainScreen].bounds.size.width > 480 ? YES : NO)
#define device_width 480.0f
#define device_height 320.0f
#if (is_ipad_hd == YES) // Error here
#define device_width = 2048.0f
#define device_height = 1496.0f
#endif
Why it works in simple tutorials and when we try something more complex these things happen!
These are preprocessor directives, so you don't have access to [UIScreen mainScreen] methods and all other objects that are defined upon compilation!
Macro evaluation happens at compile time.
However, (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) or [UIScreen mainScreen] can only be interpreted at run time.
Beyond that, you should not be relying on the screen size to do your dirty work for you. You should instead be relying on the user interface idiom and the scale of the main screen:
BOOL isiPad = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad;
BOOL isHD = [[UIScreen mainScreen] scale] == 2.0;
if (isiPad) {
if (isHD) {
// retina iPad
} else {
// non-retina iPad
}
} else {
if (isHD) {
// retina iPhone/iPod touch
} else {
// non-retina iPhone/iPod touch
}
}
Agree with others here although I'm not as well versed in the C preprocessor a quick googling came back with this:
expression is a C expression of integer type, subject to stringent restrictions. It may contain....
much better formatting than I can quickly achieve here on the source.

Using constant to declare an array of item

Is it possible to use some thing like this in Objective-C:
#define number_of_items 10
and then using it as:
int arr[number_of_items];
Yes, assuming you mean Objective C. It's pretty much a superset of "proper" C so this is perfectly okay. It's also okay in both C and C++.
You can see that it works in the following transcript:
pax> cat qq.m
#import <objc/Object.h>
// First method.
#define number_of_items 10
int arr[number_of_items];
// Second method.
#define NUMBER_OF_ROWS 10
#interface test : Object{ int xyzzy[NUMBER_OF_ROWS]; }
#end;
pax> vi qq.m ; gcc -o qq.o -c qq.m -lobjc
pax> # no errors occurred
And, now that we've finally seen what you're actually using:
#define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
#define NUMBER_OF_ROWS_ (IS_IPAD? 18: 18)
NUMBER_OF_ROWS_ is not a constant, since it depends on the return value of the function UI_USER_INTERFACE_IDIOM().
In other words, it cannot be calculated at compile time. That's why you're getting the error. You can see this by compiling the following code:
#define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
#define NUMBER_OF_ROWS_ (IS_IPAD ? 18: 20)
int UI_USER_INTERFACE_IDIOM(void) {return 20;}
int UIUserInterfaceIdiomPad;
int main (void) {
int arr[NUMBER_OF_ROWS_];
return 0;
}
Under gcc --pedantic, you get:
qq.m: In function ‘main’:
qq.m:8: warning: ISO C90 forbids variable length array ‘arr’
You either need to use a dynamically adjustable collection like NSMutableArray or use an array of the maximum size desired and only use what you need of that.

iOS blurred text: detecting & solving it once and for all?

More than once I've encountered the situation where a UIView (subclass) ends up on a fractional offset, e.g. because its dimensions are odd and it's centered, or because its location is based on the center of an odd-sized container.
This results in blurred text (or images), because iOS will try to render the view (and subviews) on half-pixel offsets. I feel that calling CGRectIntegral() for every frame-change is not a perfect solution.
I'm looking for the best way to detect those situations easily. While writing this question I came up with quite a drastic approach, which revealed more off-by-½'s in my current project than I could imagine.
So this is for sharing. Comments and suggestions for better or less drastic alternatives are more than welcome.
main.m
#import <objc/runtime.h>
#import "UIViewOverride.h"
int main(int argc, char *argv[]) {
#ifdef DEBUGVIEW
Method m1,m2;
IMP imp;
m1 = class_getInstanceMethod([UIView class], #selector(setFrame:));
m2 = class_getInstanceMethod([UIViewOverride class], #selector(setFrameOverride:));
imp = method_getImplementation(m2);
class_addMethod([UIView class], #selector(setFrameOverride:), imp, method_getTypeEncoding(m1));
m2 = class_getInstanceMethod([UIView class], #selector(setFrameOverride:));
method_exchangeImplementations(m1,m2);
m1 = class_getInstanceMethod([UIView class], #selector(setCenter:));
m2 = class_getInstanceMethod([UIViewOverride class], #selector(setCenterOverride:));
imp = method_getImplementation(m2);
class_addMethod([UIView class], #selector(setCenterOverride:), imp, method_getTypeEncoding(m1));
m2 = class_getInstanceMethod([UIView class], #selector(setCenterOverride:));
method_exchangeImplementations(m1,m2);
#endif
// etc
UIViewOverride.m
This is implemented as a UIView subclass, which avoids casts and/or compiler warnings.
#define FRACTIONAL(f) (fabs(f)-floor(fabs(f))>0.01)
#implementation UIViewOverride
#ifdef DEBUGVIEW
-(void)setFrameOverride:(CGRect)newframe
{
if ( FRACTIONAL(newframe.origin.x) || FRACTIONAL(newframe.origin.y) )
{
[self setBackgroundColor:[UIColor redColor]];
[self setAlpha:0.2];
NSLog(#"fractional offset for %#",self);
}
[self setFrameOverride:newframe]; // not a typo
}
-(void)setCenterOverride:(CGPoint)center
{
[self setCenterOverride:center]; // not a typo
if ( FRACTIONAL(self.frame.origin.x) || FRACTIONAL(self.frame.origin.y) )
{
[self setBackgroundColor:[UIColor greenColor]];
[self setAlpha:0.2];
NSLog(#"fractional via center for %#",self);
}
}
#endif
Problematic views will generate log messages and turn up transparent and either red or green.
-DDEBUGVIEW to be set as compiler flag in Debug mode.
You can get this same functionality through the CoreAnimation instrument and its misaligned flag.

Why does backtrace not contain Objective-C symbols regardless of -rdynamic?

Update: I'm working with the GNU-runtime on Linux. The problem does not occur on MacOS with the Apple-runtime.
Update 2: I compiled the GNU-runtime on MacOS and build the example with it. The error does not occur on MacOS with the GNU-runtime. I would say the problem is the glibc (since backtrace and backtrace_symbols are glibc extensions).
When printing a backtrace in a GCC compiled Objective-C app using backtraceand backtrace_symbols, I don't get any Objective-C symbols. Only the filenames, addresses and C-symbols appear.
I compiled with -g and linked with -rdynamic.
My test app:
void _printTrace()
{
void *addr[1024];
int aCount = backtrace(addr, 1024);
char **frameStrings = backtrace_symbols(addr, aCount);
for (int i = 0; i < aCount; i++) {
printf("%s\n", frameStrings[i]);
}
free(frameStrings);
}
#interface TheObject
+ (void)_printTrace;
+ (void)printTrace;
#end
#implementation TheObject
+ (void)_printTrace
{
_printTrace();
}
+ (void)printTrace
{
[self _printTrace];
}
#end
void printTrace()
{
[TheObject printTrace];
}
int main(int argc, char **argv)
{
printTrace();
return 0;
}
and it's output:
./test.bin(_printTrace+0x1f) [0x8048e05]
./test.bin() [0x8048e60]
./test.bin() [0x8048e8b]
./test.bin(printTrace+0x34) [0x8048ec5]
./test.bin(main+0xf) [0x8048eda]
/lib/libc.so.6(__libc_start_main+0xe5) [0xb7643bb5]
./test.bin() [0x8048b51]
Is there a way to let the Objective-C symbols appear in this backtrace?
dladdr() only reports global and weak symbols. But all Objective-C function symbols are local:
$ readelf -s so_backtrace
Symbol table '.dynsym' contains 29 entries:
…
Symbol table '.symtab' contains 121 entries:
Num: Value Size Type Bind Vis Ndx Name
…
49: 08048a01 13 FUNC LOCAL DEFAULT 14 _c_TheObject___printTrace
50: 08048a0e 47 FUNC LOCAL DEFAULT 14 _c_TheObject__printTrace
…
You can verify that local symbols are never returned by looking at the GNU libc source code yourself. backtrace_symbols() is defined in sysdeps/generic/elf/backtracesyms.c. It relies on _dl_addr(), which is defined in elf/dl-addr.c, to provide it with the symbol names. That ultimately calls determine_info(). If it can, it uses the the GNU hash table, which does not include local symbols by design:
49 /* We look at all symbol table entries referenced by the hash
50 table. */
…
60 /* The hash table never references local symbols so
61 we can omit that test here. */
If the GNU hash table isn't present, it falls back to standard hash table. This includes all the symbols, but the determine_info() code filters out all but the global symbols and weak symbols:
90 if ((ELFW(ST_BIND) (symtab->st_info) == STB_GLOBAL
91 || ELFW(ST_BIND) (symtab->st_info) == STB_WEAK)
To symbolicate the Objective-C function addresses, you would have to perform the look-up yourself and not filter out the local function symbols. Further, you would have to demangle the Objective-C function symbols to restore _c_TheObject___printTrace to +[TheObject _printTrace].
GNUstep's NSException implementation doesn't use backtrace, instead it uses libbfd (binary file descriptor). I think the function that actually does the work is called static void find_address, which you can view here. Using this trivial example, I get the results that follow.
#include <Foundation/Foundation.h>
#interface Test : NSObject {}
+ (void) test;
#end
#implementation Test
+ (void) test
{
Class GSStackTrace = objc_getClass("GSStackTrace");
id stack = [GSStackTrace currentStack];
for (int i = 0; i < [stack frameCount]; i++)
{
NSLog (#"%#", [[stack frameAt:i] function]);
}
}
#end
int main(int argc, char **argv)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[Test test];
[pool release];
return 0;
}
Output (when compiled with debug symbols):
2010-10-18 14:14:46.188 a.out[29091] +[GSStackTrace currentStack]
2010-10-18 14:14:46.190 a.out[29091] +[Test test]
2010-10-18 14:14:46.190 a.out[29091] main
2010-10-18 14:14:46.190 a.out[29091] __libc_start_main
You may be able to pick apart GSStackTrace. It is a “private” class (that's why I need to use objc_getClass, you'll also get lots of unrecognised selector warnings), but it seems to contain all the code necessary to read Objective-C class names.
Tested on Ubuntu 9.04 with GNUstep configured with --enable-debug (so that GSFunctionInfo is included in the build).
I expect you'll need to ask the ObjC run time about the addresses to get symbol information. The addresses returned from backtrace() could probably be passed to something like object_getClass() to get the class, for example. I haven't tried any of this but it's where I'd look next in this case.