Question
How can I debug and log C structures such as CGPoint or CGRect in Objective-C without manually writing every property each time?
For example, I would like to write something similar to:
CGPoint cgPoint = CGPointMake(0, 0);
NSLog(@"%@", cgPoint);
I understand that %@ does not work here because CGPoint is not an Objective-C object. What is the correct approach?
Short Answer
You will learn why Objective-C's %@ format specifier cannot print C structs, how Apple-provided conversion functions make Core Graphics types readable, and how to log your own structs safely.
Concept
CGPoint, CGSize, and CGRect are C structures. A structure groups several values into one value, but it is not an Objective-C object.
NSLog uses format specifiers:
%@expects an Objective-C object, such as anNSString,NSArray, orNSNumber.%d,%f, and similar specifiers expect scalar C values.- A C struct has its own memory layout, so it cannot be passed where
%@expects an object pointer.
For common Core Graphics structures, the framework provides functions that convert the struct into an NSString:
NSStringFromCGPointNSStringFromCGSizeNSStringFromCGRect
This is useful because a readable log can quickly show incorrect coordinates, sizes, or layout frames while debugging an app.
For a custom C struct, there is no automatic, universal %@ formatter. Usually, you write a small helper function that creates a descriptive string from its fields.
Mental Model
Think of %@ as a label printer designed for objects. An Objective-C object carries information about how it can describe itself.
A CGPoint is more like a sealed package containing two numbers, x and y. The label printer cannot open and interpret that package automatically.
NSStringFromCGPoint is a translator: it opens the package and produces a text label such as {0, 0} that NSLog can print with %@.
Syntax and Examples
Use the conversion function that matches the Core Graphics type.
CGPoint point = CGPointMake(12.5, 40.0);
CGSize size = CGSizeMake(320.0, 44.0);
CGRect frame = CGRectMake(10.0, 20.0, 320.0, 44.0);
NSLog(@"point: %@", NSStringFromCGPoint(point));
NSLog(@"size: %@", NSStringFromCGSize(size));
NSLog(@"frame: %@", NSStringFromCGRect(frame));
Each NSStringFrom... function returns an NSString. Because the returned value is an object, %@ is now the correct format specifier.
A possible log output is:
point: {12.5, 40}
size: {320, 44}
frame: {{10, 20}, {320, 44}}
For a custom struct, format its individual members in a helper:
typedef struct {
NSInteger identifier;
CGFloat opacity;
} LayerInfo;
static NSString *NSStringFromLayerInfo(LayerInfo info) {
return [NSString stringWithFormat:@"{ identifier: %ld, opacity: %.2f }",
(long)info.identifier,
info.opacity];
}
LayerInfo info = { .identifier = 42, .opacity = 0.75 };
NSLog(@"layer: %@", NSStringFromLayerInfo(info));
The cast to (long) matches %ld, which is a common safe choice for logging NSInteger.
Step by Step Execution
Consider this code:
CGPoint point = CGPointMake(8.0, 16.0);
NSString *pointDescription = NSStringFromCGPoint(point);
NSLog(@"Selected position: %@", pointDescription);
CGPointMake(8.0, 16.0)creates aCGPointstruct whosexis8.0and whoseyis16.0.NSStringFromCGPoint(point)reads the struct and creates an Objective-CNSStringrepresentation, similar to@"{8, 16}".pointDescriptionstores a pointer to thatNSStringobject.%@tellsNSLogto print the object description.- The console receives a readable message similar to:
Selected position: {8, 16}
By contrast, this is invalid:
NSLog(@"%@", point);
point is a struct value, not an object pointer that %@ can use.
Real World Use Cases
Core Graphics structs occur frequently in Apple-platform applications:
- Touch handling: log a tap location with
NSStringFromCGPoint(location). - Auto Layout and view debugging: log
view.frame,view.bounds, orview.centerwithNSStringFromCGRectandNSStringFromCGPoint. - Image and drawing code: inspect crop rectangles, drawing bounds, and canvas sizes.
- Animation debugging: verify a view's starting and ending frames.
- Geometry calculations: compare expected and actual positions after applying offsets, scaling, or coordinate conversion.
Example when investigating a layout issue:
NSLog(@"card frame: %@, container bounds: %@",
NSStringFromCGRect(cardView.frame),
NSStringFromCGRect(containerView.bounds));
Real Codebase Usage
In production code, logging is usually made consistent and easy to remove or filter.
Log meaningful context
Avoid logging an isolated rectangle when the surrounding state matters:
NSLog(@"[ProfileHeader] avatar frame=%@ hidden=%@",
NSStringFromCGRect(self.avatarView.frame),
self.avatarView.hidden ? @"YES" : @"NO");
Put custom formatting in one helper
If a custom struct appears in several places, create one formatter rather than repeating format strings:
typedef struct {
CGFloat red;
CGFloat green;
CGFloat blue;
} RGBColor;
static NSString *NSStringFromRGBColor(RGBColor color) {
return [NSString stringWithFormat:@"RGB(%.2f, %.2f, %.2f)",
color.red, color.green, color.blue];
}
This keeps logs uniform and ensures that a future struct change is updated in one location.
Use the debugger for one-off inspection
For a variable you only need to inspect once, LLDB can often print it directly:
(lldb) p point
Use source-level logging when the value must be observed during an asynchronous flow, reproduced in a test run, or recorded alongside other application events.
Common Mistakes
Passing a struct to %@
Broken code:
CGPoint point = CGPointMake(1, 2);
NSLog(@"%@", point);
Why it fails: %@ expects an Objective-C object pointer, while point is a struct.
Fix:
NSLog(@"%@", NSStringFromCGPoint(point));
Treating a struct as a pointer
Broken code:
NSLog(@"%@", &point);
&point is a pointer to struct memory, not an Objective-C object. %@ still does not apply.
Using the wrong numeric format specifier in a custom formatter
Broken code:
NSInteger count = 10;
NSLog(@"count: %d", count);
NSInteger can have a different size from int. Prefer %ld with a cast:
NSLog(@"count: %ld", (long)count);
Logging only part of a rectangle
NSLog(@"width: %f", frame.size.width);
This is valid, but it can hide a wrong origin or height. During geometry debugging, logging the entire CGRect is often more useful:
NSLog(@"frame: %@", NSStringFromCGRect(frame));
Assuming every struct has a built-in conversion function
The NSStringFrom... helpers are supplied for common framework types. Your own struct needs its own formatting helper unless its framework documents one.
Comparisons
| Value being logged | What it is | Recommended approach |
|---|---|---|
NSString *name | Objective-C object | NSLog(@"%@", name) |
NSInteger count | Integer scalar | NSLog(@"%ld", (long)count) |
CGFloat amount | Floating-point scalar | NSLog(@"%g", (double)amount) |
CGPoint point | C struct | NSLog(@"%@", NSStringFromCGPoint(point)) |
CGSize size | C struct | NSLog(@"%@", NSStringFromCGSize(size)) |
CGRect rect | C struct | NSLog(@"%@", NSStringFromCGRect(rect)) |
| Custom struct | C struct | Write a helper that returns NSString * |
NSValue can also box some structs into an Objective-C object:
NSValue *boxedPoint = [NSValue valueWithCGPoint:point];
NSLog(@"%@", boxedPoint);
This can be convenient when an API requires an object, such as storing a point in an array. For clear logging, NSStringFromCGPoint(point) directly communicates your intention.
Cheat Sheet
// Core Graphics structs
NSLog(@"%@", NSStringFromCGPoint(point));
NSLog(@"%@", NSStringFromCGSize(size));
NSLog(@"%@", NSStringFromCGRect(rect));
// Scalars
NSLog(@"integer: %ld", (long)integerValue);
NSLog(@"decimal: %g", (double)decimalValue);
// Custom struct: create a formatter that returns NSString *
static NSString *NSStringFromMyStruct(MyStruct value) {
return [NSString stringWithFormat:@"{ a: %d, b: %.2f }", value.a, value.b];
}
NSLog(@"%@", NSStringFromMyStruct(value));
Rules:
%@is for Objective-C objects.- A C struct is not an Objective-C object.
- Convert framework structs to strings before using
%@. - Do not pass a struct or a pointer-to-struct to
%@. - Use a named helper for custom struct descriptions reused across a project.
FAQ
Can I use %@ directly with a CGPoint?
No. CGPoint is a C struct, while %@ expects an Objective-C object.
How do I print a CGRect with NSLog?
Use NSStringFromCGRect:
NSLog(@"%@", NSStringFromCGRect(rect));
How do I log a CGSize?
Use NSStringFromCGSize(size) and print the resulting string with %@.
Can I use NSValue to log a CGPoint?
Yes. NSValue can box supported structs into an object. It is especially useful when an API needs an object, but NSStringFromCGPoint is more explicit for logging.
Is there a generic format specifier for every C struct?
No. Format strings must know the type and layout of their arguments. Use a framework conversion function when available, or write a formatter for your own struct.
Why cast NSInteger to long when logging?
NSInteger changes size between architectures. (long) together with %ld avoids a format mismatch.
What should I use to inspect a struct just once during debugging?
Use LLDB's p command, such as p point, when you do not need the value recorded by application logging.
Mini Project
Description
Build a small geometry-debugging utility for a view layout. The utility logs a panel's frame, its center point, and whether the frame fits inside a container rectangle. It demonstrates converting Core Graphics structs to readable strings before logging them.
Goal
Log readable geometry information and identify whether a panel frame is contained within its container.
Requirements
Create a CGRect for a container and a CGRect for a panel.
Log both rectangles using the appropriate NSStringFrom... conversion function.
Log the panel's center as a CGPoint.
Use CGRectContainsRect to determine whether the panel fits inside the container.
Log a clear success or warning message based on the result.
Keep learning
Related questions
2D Array Loop Order and Cache Performance in C
Learn why swapping nested loops changes 2D array performance in C, using row-major memory layout, cache locality, and practical benchmarks.
Array-to-Pointer Conversion in C and C++ Explained
Learn what array-to-pointer conversion means in C and C++, how array decay works, and how it differs from a pointer to an array.
Building More Fault-Tolerant Embedded C++ Applications for Radiation-Prone ARM Systems
Learn practical C++ and compile-time techniques to reduce soft-error damage in embedded ARM systems exposed to radiation.