The user can choose whether he wants to use an existing image or take a new one for use as the background.
Everything works as far as the FirstViewController is concerned. Sadly the image is no longer set as the background, when the SecondViewController is displayed.
How do I enable the use of the image as background for the SecondViewController?
I used this piece of code (in the SecondViewController):
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
int bla = [prefs integerForKey:#"background_key"];
if (bla == 1) {
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"1.mac.jpg"]];
} else if (bla == 2) {
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"black.jpg"]];
} else if (bla == 3) {
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"pinn.png"]];
} else if (bla == 4) {
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"MyImage.png"]];
}
I tried to save the chosen image as "MyImage.png" but it looks as though it did not work. The background is black instead of the image selected.
I used this code (from the 1stViewController) to save the chosen picture as "MyImage.png":
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)anInfo {
//UIImage *selectedImage;
NSURL *mediaUrl;
mediaUrl = (NSURL *)[anInfo valueForKey:UIImagePickerControllerMediaURL];
if (mediaUrl == nil)
{
selectedImage = (UIImage *) [anInfo valueForKey:UIImagePickerControllerEditedImage];
if (selectedImage == nil)
{
selectedImage = (UIImage *) [anInfo valueForKey:UIImagePickerControllerOriginalImage];
NSLog(#"Original image picked.");
}
else
{
NSLog(#"Edited image picked.");
}
}
[picker dismissModalViewControllerAnimated:YES];
if (selectedImage != nil)
{
self.view.backgroundColor = [UIColor colorWithPatternImage:selectedImage];
}
// Get the image from the result
UIImage* ownImage = [anInfo valueForKey:UIImagePickerControllerOriginalImage];
// Get the data for the image as a PNG
NSData* imageData = UIImagePNGRepresentation(ownImage);
// Give a name to the file
NSString* imageName = #"MyImage.png";
// Now, we have to find the documents directory so we can save it
// Note that you might want to save it elsewhere, like the cache directory,
// or something similar.
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
// Now we get the full path to the file
NSString* fullPathToFile = [documentsDirectory stringByAppendingPathComponent:imageName];
// and then we write it out
[imageData writeToFile:fullPathToFile atomically:NO];
return;
}
There must be a mistake or logical mistake.
Help would be appreciated!
ImageNamed is generally used to load images that are included as part of the resources of an application.
I suspect you want:
UIImage* image = [UIImage imageWithContentsOfFile:fileNameAndPath];
Try loading the image with similar code to how you save it instead of using +imageNamed:: build the full path and load it from there.
+imageNamed: "looks for an image with the specified name in the application’s main bundle." (UIImage reference), but that's not where you are saving it to. I don't think you can write into the application's bundle, so you won't be able to use +imageNamed: here.
Related
I have a little flow problem with my UICollectionView. I want to display PDF's thumbnails just like in Apple's iBook app, when I scroll my collection view I can see it's not really smooth. Here is the way I use to load my pictures :
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath: (NSIndexPath *)indexPath
{
GridCell *cell = [cv dequeueReusableCellWithReuseIdentifier:#"gridCell" forIndexPath:indexPath];
...
// Set Thumbnail
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if ([Tools checkIfLocalFileExist:cell.pdfDoc])
{
UIImage *thumbnail = [Tools generateThumbnailForFile:((PDFDocument *)[self.pdfList objectAtIndex:indexPath.row]).title];
dispatch_async( dispatch_get_main_queue(), ^{
[cell.imageView setImage:thumbnail];
});
}
});
...
return cell;
}
Method to get thumbnail :
+ (UIImage *)generateThumbnailForFile:(NSString *) fileName
{
// ----- Check if thumbnail already exist
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* thumbnailAddress = [documentsPath stringByAppendingPathComponent:[[fileName stringByDeletingPathExtension] stringByAppendingString:#".png"]];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:thumbnailAddress];
if (fileExists)
return [UIImage imageWithContentsOfFile:thumbnailAddress];
// ----- Generate Thumbnail
NSString* filePath = [documentsPath stringByAppendingPathComponent:fileName];
CFURLRef url = (__bridge CFURLRef)[NSURL fileURLWithPath:filePath];
CGPDFDocumentRef documentRef = CGPDFDocumentCreateWithURL(url);
CGPDFPageRef pageRef = CGPDFDocumentGetPage(documentRef, 1);
CGRect pageRect = CGPDFPageGetBoxRect(pageRef, kCGPDFCropBox);
UIGraphicsBeginImageContext(pageRect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, CGRectGetMinX(pageRect),CGRectGetMaxY(pageRect));
CGContextScaleCTM(context, 1, -1);
CGContextTranslateCTM(context, -(pageRect.origin.x), -(pageRect.origin.y));
CGContextDrawPDFPage(context, pageRef);
// ----- Save Image
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
NSData *imageData = [NSData dataWithData:UIImagePNGRepresentation(finalImage)];
[imageData writeToFile:thumbnailAddress atomically:YES];
UIGraphicsEndImageContext();
return finalImage;
}
Do you have any suggestion ?
Take a look at https://github.com/rs/SDWebImage. The library works great for async image loading particularly the method setImageWithURL:placeholderImage:
You can call that method and set a place holder with a loading image or blank png and once the image you are trying to retrieve is loaded it will fill in the place holder. This should speed up your app quite a bit.
In my app, I use UIImageView and UIScrollView to show a lot of images (every time there are about 20 images and every image is about 600px*500px and size is about 600kb).
I use this code to accomplish this function:
// Here is pictures Data;
self.klpArry = self.pictureData;
CGSize size = self.klpScrollView1.frame.size;
for (int i=0; i < [klpArr count]; i++) {
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake((size.width * i)+300, 20, 546, 546)];
NSString *filePath = [[NSBundle mainBundle] pathForResource:[klpArr objectAtIndex:i] ofType:#"jpg"];
UIImage *imageData = [[UIImage alloc]initWithData:[NSData dataWithContentsOfFile:filePath]];
[iv setImage:imageData];
iv.backgroundColor = [UIColor grayColor];
[self.klpScrollView1 addSubview:iv];
imageData = nil;
iv = nil;
iv.image = nil;
filePath = nil;
[imageData release];
[filePath release];
[iv release];
}
// show the picture in scrollview;
[self.klpScrollView1 setContentSize:CGSizeMake(size.width * numImage, size.height)];
self.klpScrollView1.pagingEnabled = YES;
self.klpScrollView1.showsHorizontalScrollIndicator = NO;
self.klpScrollView1.backgroundColor = [UIColor grayColor];
But every time I initialize this function, the memory will increase about 5MB. Actually I release UIImageView, UIimage and UIScrollView (vi.image=nil, [vi release]) but it doesn't work, the allocated memory is not getting released. BTW, I used my friend's code first vi.image = nil then vi = nil; but the pictures are not getting displayed on scrollview.
The main problem, as I see it, is that you're setting your local variables to nil and then you proceed to try to use those local variables in methods like release and the like, but because they've been set to nil, those methods now do nothing, and the objects with the +1 retainCount (or now +2 because you've added them to your view) are never released.
Thus, I'd suggest the following:
//Here is pictures Data;
self.klpArry = self.pictureData;
CGSize size = self.klpScrollView1.frame.size;
for (int i=0; i < [klpArr count]; i++) {
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake((size.width * i)+300, 20, 546, 546)];
NSString *filePath = [[NSBundle mainBundle] pathForResource:[klpArr objectAtIndex:i] ofType:#"jpg"];
UIImage *imageData = [[UIImage alloc]initWithData:[NSData dataWithContentsOfFile:filePath]];
[iv setImage:imageData];
iv.backgroundColor = [UIColor grayColor];
[self.klpScrollView1 addSubview:iv];
// Don't set these to nil, or else subsequent release statements do nothing!
// These statements are actually not necessary because they refer to local
// variables so you don't need to worry about dangling pointers. Make sure
// you're not confusing the purpose of setting a pointer to nil in ARC to
// what you're doing in your non-ARC code.
//
// imageData = nil;
// iv = nil;
// You don't want to set this to nil because if iv is not nil, your image
// will be removed from your imageview. So, not only is this not needed,
// but it's undesired.
//
// iv.image = nil;
// This statement is not necessary for the same reason you don't do it
// to imageData or iv, either. This is probably even worse, though, because
// filePath is not a variable that you initialized via alloc. You should
// only be releasing things you created with alloc (or new, copy, mutableCopy,
// for which you issued a retain statement).
//
// filePath = nil;
[imageData release];
// filePath is a +0 retainCount already, so don't release. You only release
// those items for which you increased retainCount (e.g. via new, copy,
// mutableCopy, or alloc or anything you manually retained).
//
// [filePath release];
[iv release];
}
// show the picture in scrollview;
[self.klpScrollView1 setContentSize:CGSizeMake(size.width * numImage, size.height)];
self.klpScrollView1.pagingEnabled = YES;
self.klpScrollView1.showsHorizontalScrollIndicator = NO;
self.klpScrollView1.backgroundColor = [UIColor grayColor];
Thus, your code would be simplified (and corrected) to probably just be:
//Here is pictures Data;
self.klpArry = self.pictureData;
CGSize size = self.klpScrollView1.frame.size;
for (int i=0; i < [klpArr count]; i++) {
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake((size.width * i)+300, 20, 546, 546)];
NSString *filePath = [[NSBundle mainBundle] pathForResource:[klpArr objectAtIndex:i] ofType:#"jpg"];
UIImage *imageData = [[UIImage alloc] initWithData:[NSData dataWithContentsOfFile:filePath]];
[iv setImage:imageData];
iv.backgroundColor = [UIColor grayColor];
[self.klpScrollView1 addSubview:iv];
[imageData release];
[iv release];
}
// show the picture in scrollview;
[self.klpScrollView1 setContentSize:CGSizeMake(size.width * numImage, size.height)];
self.klpScrollView1.pagingEnabled = YES;
self.klpScrollView1.showsHorizontalScrollIndicator = NO;
self.klpScrollView1.backgroundColor = [UIColor grayColor];
Or you could further simplify your code through the use of autorelease:
//Here is pictures Data;
self.klpArry = self.pictureData;
CGSize size = self.klpScrollView1.frame.size;
for (int i=0; i < [klpArr count]; i++) {
UIImageView *iv = [[[UIImageView alloc] initWithFrame:CGRectMake((size.width * i)+300, 20, 546, 546)] autorelease];
NSString *filePath = [[NSBundle mainBundle] pathForResource:[klpArr objectAtIndex:i] ofType:#"jpg"];
UIImage *imageData = [[[UIImage alloc] initWithData:[NSData dataWithContentsOfFile:filePath]] autorelease];
[iv setImage:imageData];
iv.backgroundColor = [UIColor grayColor];
[self.klpScrollView1 addSubview:iv];
}
// show the picture in scrollview;
[self.klpScrollView1 setContentSize:CGSizeMake(size.width * numImage, size.height)];
self.klpScrollView1.pagingEnabled = YES;
self.klpScrollView1.showsHorizontalScrollIndicator = NO;
self.klpScrollView1.backgroundColor = [UIColor grayColor];
By the way, the statement (with autorelease):
UIImage *imageData = [[[UIImage alloc] initWithData:[NSData dataWithContentsOfFile:filePath]] autorelease];
could probably be simplified to:
UIImage *imageData = [UIImage imageWithContentsOfFile:filePath];
This gives you a UIImage, with a +0 retainCount (i.e. you don't have to release it) from your file.
So, a few final observations:
You really should probably review and study the Advanced Memory Management Programming Guide. It's dense reading if you're new to memory management, but mastery of these concepts (especially in non-ARC code) is critical.
If you haven't, I'd encourage you to run your code through the static analyzer ("Product" - "Analyze" or shift+command+B). I'd be surprised if many (if not most) of these issues wouldn't have been identified for you by the analyzer. You should have zero warnings when you run your code through the analyzer.
If you want to take this to the next level, you might want to be far more conservative about your memory management. The governing principle would be a system design that only loads the images that are needed at the UI at any given time, which involves not only calvinBhai's excellent suggestion of lazy loading of images (i.e. don't load images into memory until your UI really needs them), but also a pro-active releasing images once they've scrolled off the screen. Maybe you don't need to worry about it in your app, because you're only dealing with 20 images at a time, but if any of your portfolios/galleries expanded to 100 or 1000 images, the idea of keeping all of those in memory at any given time is just a bad idea. This is a more advanced concept, so maybe you should focus on the basic memory management problems of your existing code first, but longer term, you might want to contemplate lazy loading and pro-active releasing of images.
If memory is your concern,
try lazy loading the images = Load the visible image, the next and previous image. You dont have to have all images added to your klpscrollview.
Once you figure out lazy loading the images onto your scrollview, then you can think of fixing the images not showing on your scrollview.
Easier would be to search for "lazy load uiimage uiscrollview"
In my view I have several fields, some of them are hidden. There is a segmentController with 2 option. To enable the "take a photo" button it must be set on 1. So I fill the fields i see, press the segmentedController on 1 position, fill another field which was hidden and take a picture.When I came back from the photo view, all the fields are empty and the selector is deselected (setSelectedSegmentIndex:-1).If I press again this selector on 1 position, the hidden field is empty, but the UIImageView show the photo i've taken...if I set in viewDidLoad setSelectedSegmentIndex:1, as I dismiss the photoView, all the fields are showed empty (again the UIImageview show the picture).I also tried to save the contents of every field in variables and put everything back in viewDidLoad in this way
if (([name length] != 0) || ([price length] != 0) || (category length] != 0)) {
//restore all the fields from those NSString variables and set segment on 1
}
but the app crashs.I have this problem just if I run the app on the iPhone, not on the simulator, because the simulator takes its image right from the camera roll.
This is the code for taking photos.
-(IBAction)takePhoto:(id)sender {
if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]) {
self.imgPicker.sourceType = UIImagePickerControllerSourceTypeCamera;
self.imgPicker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;
} else {
imgPicker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
}
[self presentModalViewController:self.imgPicker animated:YES];
}
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
[picker dismissModalViewControllerAnimated:YES];
int r = arc4random() % 9999;
NSDate *date = [NSDate date];
NSString *photoName = [dateNameFormatter stringFromDate:date];
photoName = [photoName stringByAppendingString:[NSString stringWithFormat:#"%d", r]];
if (imagePath) {
[imagePath release];
}
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
imagePath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.png", photoName]];
[imagePath retain];
UIImage *picture = [[info objectForKey:UIImagePickerControllerOriginalImage] retain];
// ----- CODE FOR SCALE THE IMAGE ----- //
if (picture.size.width == 1936) {
picture = [picture scaleToSize:CGSizeMake(480.0f, 720.0f)];
} else {
picture = [picture scaleToSize:CGSizeMake(720.0f, 480.0f)];
}
photoPreview.image = picture;
photoPreview.contentMode = UIViewContentModeScaleAspectFit;
CGRect frame = photoPreview.frame;
if (picture.size.width == 480) {
frame.size.width = 111.3;
frame.size.height =167;
} else {
frame.size.width = 167;
frame.size.height =111.3;
}
photoPreview.frame = frame;
// ----- ----- - END CODE - ----- ----- //
NSData *webData = UIImagePNGRepresentation(picture);
//CGImageRelease([picture CGImage]);
[webData writeToFile:imagePath atomically:YES];
}
-(void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[picker dismissModalViewControllerAnimated:YES];
}
when using the UIImagePickerController the system usually calls a memory warning. This cause all of the views that are loaded to unload in order to free some memory.
My guess that in your case, you are not saving the state correctly and on the second call to the "viewDidLoad:" and you are unable to re-initiate the view.
you should write the viewDidLoad in a way that it can be called more than once and will still behave the same.
My app has a user select a photo from the picker. I apply the selected image to a view and then I save it to a file and reference that file in user defaults so when the UserProfile is created that avatar is loaded in.
When I close the app and then start it again, the app loads the image from the file. After loading the image from the file, my app is crashing when I apply it to an Image view because it is seen as an __NSCFArray. There is no method scale on __NSCFArray. Why is it being cast to this class?
-[__NSCFArray scale]: unrecognized selector sent to instance 0x145260
* Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '[__NSCFArray scale]:
unrecognized selector sent to instance 0x145260'
Here is my code where the UIImage is created from a file:
#implementation UserProfile
- (id) init {
self = [super init];
if(self) {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
_username = (NSString *)[userDefaults objectForKey:USERNAME_KEY];
NSString *filename = (NSString *) [userDefaults objectForKey:#"AvatarFilename"];
NSLog(#"filename from user defaults: %#",filename);
if (filename) {
_avatar = [UIImage imageWithContentsOfFile:filename];
if (!_avatar) NSLog(#"LOGERROR: avatar was not created from file");
_customAvatar = TRUE;
} else {
_customAvatar = FALSE;
_avatar = [UIImage imageNamed:DEFAULT_AVATAR_FILENAME];
if (!_avatar) NSLog(#"LOGERROR: avatar was not created from default");
}
[self createThumbnail];
}
return self;
}
Note: in my createThumbnail code I call this [_avatar isKindOfClass:[UIImage class] and it says it is a UIImage. But then when I set the view, it thinks it is an __NSCFArray. I don't even understand how this is possible since the property is a UIImage *.
This is how the image is persisted
- (void) setAvatar:(UIImage *)image
{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSString *filename = nil;
if (image) {
if (_avatar) [_avatar release];
_avatar = [image retain];
_customAvatar = TRUE;
filename = [NSString stringWithFormat:#"%#/%#",[MyUtilities applicationDocumentsDirectory],AVATAR_FILENAME];
if (![UIImagePNGRepresentation(image) writeToFile:filename atomically:YES])
NSLog(#"LOGERROR: Failure to write avatar file");
else NSLog(#"saved avatar to PNG file");
} else {
NSLog(#"setting default avatar");
_avatar = [UIImage imageNamed:DEFAULT_AVATAR_FILENAME];
_customAvatar = FALSE;
}
[userDefaults setObject:filename forKey:AVATAR_KEY];
// TODO If performance is crucial, consider creating a default thumbnail as well
[self createThumbnail];
if(![userDefaults synchronize])
{
NSLog(#"LOGERROR: Failure to synchronize userDefaults");
}
}
To my knowledge I am not receiving a memory warning.
You are assigning autoreleased objects to your ivars. Most likely they are being deallocated and then when you try to access one of those UIImage it happens to be an NSArray at the same memory address.
_username = (NSString *)[userDefaults objectForKey:USERNAME_KEY];
...
_avatar = [UIImage imageWithContentsOfFile:filename];
...
_avatar = [UIImage imageNamed:DEFAULT_AVATAR_FILENAME];
You need to retain them.
You are saving autoreleased object (return by imageNamed: and imageWithContentsOfFile:) without retaining in init. You can replace that method with next one:
- (id) init {
self = [super init];
if(self) {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
_username = (NSString *)[userDefaults objectForKey:USERNAME_KEY];
NSString *filename = (NSString *) [userDefaults objectForKey:#"AvatarFilename"];
NSLog(#"filename from user defaults: %#",filename);
if (filename) {
_avatar = [[UIImage imageWithContentsOfFile:filename] retain];
if (!_avatar) NSLog(#"LOGERROR: avatar was not created from file");
_customAvatar = TRUE;
} else {
_customAvatar = FALSE;
_avatar = [[UIImage imageNamed:DEFAULT_AVATAR_FILENAME] retain];
if (!_avatar) NSLog(#"LOGERROR: avatar was not created from default");
}
[self createThumbnail];
}
return self;
}
You don't need to retain this object anymore.
Just release it in dealloc.
In setAvatar: you should also retain returned by imageNamed: value : _avatar = [[UIImage imageNamed:DEFAULT_AVATAR_FILENAME] retain];
I am using the ImageDemo template and it works great if I code in an image from the bundle but when I try and load them from directories I have created in my app I have no luck.
Code:
- (AQGridViewCell *) gridView: (AQGridView *) aGridView cellForItemAtIndex: (NSUInteger) index
{
static NSString * PlainCellIdentifier = #"PlainCellIdentifier";
AQGridViewCell * cell = nil;
ImageDemoGridViewCell * plainCell = (ImageDemoGridViewCell *)[aGridView dequeueReusableCellWithIdentifier: PlainCellIdentifier];
if ( plainCell == nil )
{
plainCell = [[[ImageDemoGridViewCell alloc] initWithFrame: CGRectMake(0.0, 0.0, 200.0, 150.0)
reuseIdentifier: PlainCellIdentifier] autorelease];
plainCell.selectionGlowColor = [UIColor lightGrayColor];
}
NSString *fileName = [NSString stringWithFormat:#"%#/%#_tn.jpg",[manufacturerID stringValue], [[[self.collectionItems objectAtIndex:index] valueForKey:#"PhotoName"] stringByReplacingOccurrencesOfString:#" " withString:#"%20"]];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *savePath =[documentsPath stringByAppendingPathComponent:fileName] ;
NSData *imageData = nil;
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:savePath];
if (fileExists) {
imageData = [NSData dataWithContentsOfFile:savePath];
} else {
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"NoImageAvailableThumbNail" ofType:#"png"];
imageData = [NSData dataWithContentsOfFile:filePath];
}
UIImage *myImage = [UIImage imageWithData:imageData];
NSLog(#"%#", myImage);
if (myImage==nil) {
NSLog(#"NO IMAGE");
}
plainCell.image = myImage;
cell = plainCell;
return ( cell );
}
I code I am using to pull the image data I know works because I use it all over in other places in my app and I am checking for nil when I set the property on the AQGridViewCell:
- (void) setImage: (UIImage *) anImage
{
_imageView.image = anImage;
[self setNeedsLayout];
}
I don't see the check for a nil UIImage in your example there. Are you certain it's being loaded correctly? Try creating the image on one line and setting it on another so you can see it being created in the debugger. For reference, I do something similar with cached book cover images in the Kobo app, and that works. One difference though is that I use the C methods to load a UIImage from a PNG file. Something like UIImageFromPNGData(), off the top of my head (sorry replying by iPhone right now).
The only other thing I can think to check is whether the image view is hidden for some reason. Printing it in the debugger should tell you that, along with it's size, layout, etc.