• Me
  • Tutorials
  • Blog
  • Little Bits
  • Poker

Luke Parham

iOS Developer + Other Stuff

  • Me
  • Tutorials
  • Blog
  • Little Bits
  • Poker

Megabytes vs. Mebibytes

I saw this a while ago, but always think its kind of funny/neat. Have you ever noticed how in Instruments or Xcode, if you see an amount of memory, it won't say MB, it'll say MiB.

So what's the "i" there for? Do they just mean megabytes? Well, turns out they don't exactly.

If you look up MiB, you should find this wikipedia article explaining what a mebibyte is.

The tl;dr is that, when we talk about megabytes we're talking about a count of bytes as a multiple of 10. So one kilobyte is 1000 bytes and a megabyte is 1000^2 bytes.

sR9WU.png

If you want to think about byte counts in powers of two, then you would need to use 1024 bytes instead. And you can, but if you do, you aren't talking about kilobytes anymore. You're talking about a kibibyte. And if you want to think about 1024^2 then you're measuring your data in mebibytes!

Unfortunately, these terms are often used interchangeably.  

Just remember, if you see a system telling you a file is 1MB aka 1024KB you can smile smugly to yourself, content in your knowledge of the true unit, the humble mebibyte.

Little Bits RSS
Tuesday 06.12.18
Posted by Luke Parham
 

UITableViews are Liars

You know what's really useful information?

Knowing exactly when items in a scroll view come onscreen and then when they go offscreen. 

You would think you could get this information from a tableView's -tableView:willDisplayCell: method. And you kind of can, but not at first.

Turns out, when a table view is first loaded, this method will be called for _a bunch_ of cells. In my case it was like 7, even though only 3 were actually onscreen. Then -tableView:didEndDisplayingCell: was immediately called for those 7 cells.

giphy.gif

To get around this, I added a check to see if this call was happening when the table view's contentOffset y value was still at its original position. If it is, it's this initial round of setup that's done by the table.

When this is the case, you have to explicitly grab each cell and see if it's actually onscreen by using its frame.

If it's not, then just bail early. If it is, do any preloading you need to do.

Later, when the content offset isn't at its original position, that means you're scrolling the table. When this is the case, this method will accurately only be called right as the cell is coming onscreen.

Friday 05.04.18
Posted by Luke Parham
 

Releasing a Private Precompiled Cocoapod

I know this kind of goes against the whole purpose of Cocoapods, but sometimes you work at a stealthy startup who can't exactly go giving away all their tech secrets when they provide a framework to customers.

If you're in this boat, then fear not, there's a really easy way to provide access to your precompiled framework that doesn't involve email or a shared dropbox folder. This does assume your company uses some cloud based repo manager like Github or Bitbucket.

1. Make a Pod Repo

First, you’ll need to add a new git repo that has the precompiled .framework and a podspec. The podspec has a few required properties you’ll need to define, but it’s really just all the basic ones that are easy to fill in. Name, version, summary and description are some of the usuals that you'll have to fill in just to get things working.

The key property to set is vendored_frameworks. Pass in the name of your precompiled framework and the podspec will know that when a customer does a pod install, this framework should be included in the Pods target’s list of bundled frameworks.

2. Make a Spec Repo

A spec repo is just a repository that has a set of folders, each representing a separate cocoapod. Inside each folder, there’s another set of folders, each representing a discrete version of that framework. Usually these are tied to a tag that’s been assigned to a particular commit. Then inside that version folder, there’s a copy of the podspec at that point in time.

3. Release a New Version

The interesting thing is that you don’t manage a Spec repo directly by creating new folders for each version.

Instead, you first add your spec repo to your set of repos on the command line with:

Then, you use the pod command to create a new release for you

As long as your podspec was set up correctly and passes the linter, a new folder for this version of the pod will be pushed up to the Spec repo and will be accessible to anyone doing a pod install in the future.

Now, your pod won't automatically be visible to a client's Podfile. To fix that, they'll need to add your Specs repo as a source from which Pods can be retrieved by adding this line to the top of their Podfile.

4. Done

And that’s it!

I will say this solution absolutely doesn’t scale super well. Github doesn't seem to allow you to add organizations as read-only collaborators. You probably don't want to manage tons of clients (aka all their employees) as collaborators on GitHub, but if you only have a couple then this is a really easy way to get up and running quickly while sharing your closed source frameworks!

I'm pretty sure the nice thing is, once you get this far you can take the next step and have them use an Artifactory instance as the middle-man. This means you'll only need to add some generic Artifactory user as a collaborator. Then their instance can pull down versions of your frameworks and distribute them to the whole team.

And you thought I was leading down a dark path there for a second 

Thursday 03.22.18
Posted by Luke Parham
 

Dynamic Arrays in C

I use dynamic arrays all the time courtesy of NSMutableArray, but I realized today that I wasn't super sure how to implement an actual low level dynamically growing C array.  

I was aware that a common method of doing so was to create an array of size 1 and then to just add stuff to that array. The only caveat is that if, when adding a new element, you've filled up your array, on the next insertion you'll need to double the size of the array.  The significant thing about this strategy is that the doubling operation is classic log(n) growth.  How many times will you need to do this doubling to have an array that allows 1,000,000 elements? Something like 20, I don't remember the exact number but you've got a calculator right in front of you.

Anyway, that part seems pretty straightforward, but how exactly do you double the size of an array in C? Well turns out that's easy too. You just use realloc() which will take a pointer to an array and return a new pointer to an array of a new size with your elements copied over from the first one.

The interesting thing here, is that whether or not the array you get back happens to start at the same pointer location depends on whether the system can pull that off or not.  You always need to use a new pointer to catch whatever gets returned though. If instead you were to always overwrite the old pointer with the new one then any time it is actually a new location in memory, the old array will be lost in space and you won't be able to free it.

Wednesday 05.17.17
Posted by Luke Parham
 

iOS Launch Times

So if you're trying to make your app launch faster, there are a few things you'll want to pay attention to.  

What to Measure

When you're looking at startup time, you'll want to measure the time up until the moment after -applicationDidFinishLaunchingWithOptions: is called.

To the system, your app has completed its launch when the first CATranscation has been created and passed off to the render server. This happens right after -applicationDidFinishLaunchingWithOptions: which is why that's when you can stop measuring.

Pre-Main Launch Time

Before main() is even called, your app has to do a bit of setup.

  1. Dylib loading: First things first, the system needs to load any dynamic libraries, including system libraries like UIKit. You can speed this up by combining dylibs or switching to static libs.
  2. Rebase Binding: This can be longer or shorter depending on the number of classes in your app.
  3. Obj-C Setup: At this point the system will take all your Objective-C classes and create the method dispatch tables needed to actually call methods.
  4. Initializers: This happens when you implement +load methods in Obj-C or Swift.

Swift vs. Obj-C

One interesting thing is comparing two, very simple, identical sample apps.  One was written in ObjC and one was re-written in Swift.

First, the ObjC version:

Here we see a total pre-main time of 78.27 ms and a dylib load time of just 11.46 ms.

And then, the Swift one:

Here we have a dylib load time of 256.83ms! This is with no external dylibs being pulled into either app, which means merely using Swift and dynamically linking its standard library is ~25X slower than using Objective-C.  This is pretty strange given Apple's recommendation that your pre-main time stay under 400ms when they're taking over half of that off the top just to use Swift.

Thursday 03.23.17
Posted by Luke Parham
Comments: 1
 

Shadow Rendering

Creating shadows for your layers can easily introduce some offscreen render passes to your app. 

The following code will do just that:

But why is this any worse than using something like CoreGraphics?

tenor.gif

Well, it turns out that its just a lack of information on the part of the layer.  When a layer is trying to create a shadow for text or an image, it has no way of knowing the shape of that content up front.  Because of that, it needs to do a render pass ahead of time just to analyze the alpha component of the layer and decide where the shadow should go.

That’s why setting a shadowPath fixes things for an image.  Since you provided the path information the layer no longer needs to render the content in memory to figure things out on its own!

ほら、the problem is fiixed!

Thursday 03.16.17
Posted by Luke Parham
 

Local Image Loading

Did you know that calling [UIImage imageNamed:@“fileName.png”] automatically does caching of the images you load so that the image will be held in RAM in order to avoid another trip to the disk? 

If you have images that are loaded a couple times then this speeds things up for you, but for images that are only loaded once, it’s better to do something like:

The -imageWithContentsOfFile: method never checks the cache for an image and thus avoids this extra step when you don’t need it.

Tuesday 04.26.16
Posted by Luke Parham
 

Bounds's and Frames of a UIView

Wondering why exactly frame, bounds and center all exist for configuring a view's size and location?

Frame:

Apple: This rectangle defines the size and position of the view in its superview’s coordinate system.  

Changing the frame automatically updates the center and the size portion of the bounds property.

* If the transform property is anything but the identity transform, you shouldn't worry about using the frame.

The documentation says the frame is undefined, though according to a WWDC presentation, the frame is defined by the minimum rectangle that contains the drawn view.

As you can see, by rotating the view by 45 degrees, the frame's size technically grows from 200x200 to 282x282.  The size in the bounds property remains 200x200, which is why it should be used whenever the transform isn't set to the identity.

Bounds:

Apple: The bounds rectangle describes the view’s location and size in its own coordinate system.

While the origin of the frame defines where the view will appear inside of its superview, and will inform the center property, the origin of the bounds property defines the offset of the content within the view and has nothing to do with its location in the superview.

The default is (0, 0) but as the value is changed, what is visible within the view shifts.

If this sounds familiar, it should.  The bounds property is synonymous with the contentOffset of a UIScrollView subclass.  All a scroll view really is, is a view with content that flows beyond its size.  As you scroll, the bounds origin is adjusted, revealing the previously hidden content.

 

Monday 04.25.16
Posted by Luke Parham
 

The shouldRasterize Property of a CALayer

The shouldRasterize property of a CALayer is a hint to the system that this element and its sublayers are:

  1. Going to need offscreen rendering.
  2. Won't have sublayers that change at any point.

If this is actually the case and you set shouldRasterize to YES then the system will composite your view to an image once with the GPU. 

The cache size for these rasterized views is 2.5x the screen size.

The rasterized images are evicted from the cache if they're unused for more than 100ms.

Any time the necessary image isn't in the cache an offscreen render pass occurs.

The catch: If you enable this property on a layer that won't actually benefit from rasterization or fit these requirements, you could unknowingly introduce an offscreen render pass on every frame, hurting performance dramatically.

A solution: When you profile your app with the Core Animation instrument, if you check the Color Hits Green and Misses Red option on the right side you'll be alerted to any cache misses that would result in an offscreen pass.

Friday 03.25.16
Posted by Luke Parham
 

Progressive JPEGs

Turns out there are two ways a jpeg can be saved.  

Often times, jpegs are baseline images which means they load from top to bottom, revealing a little bit of the image at a time.

Alternatively, progressive jpegs show the entire image up front (at a reduced quality) and progressively load the full quality image.  

On a webpage this means you'll get faster load times and can avoid the ugly experience of seeing full quality chunks of the image as it loads.  

On iOS it means you can use something like PINRemoteImage or ASNetworkImageNode to get something on the screen much more quickly than waiting for a full quality jpeg to load.

If you happen to be using Paperclip to do image upload and processing on the server-side you can do something like:

Where "-interlace Plane" creates a progressive jpeg instead of the default baseline jpeg.

Saturday 02.27.16
Posted by Luke Parham
 

Header File #import Best Practices

Since a header file's imports are then all imported into whatever files that particular header is imported into, they can cause a lot of unnecessary importing.

A good rule of thumb seems to be that you should only #import whatever class the current class is a subclass of and any protocols it conforms to.  This is a good reason to separate protocols into their own files so they can be imported where necessary without bringing along a whole other class.

All other necessary classes (for properties and method definitions) can be forward declared using the @class declaration.

Wednesday 02.24.16
Posted by Luke Parham
 

#import Order Matters

Apparently, the order of #import statements actually matters.  

You want to put the corresponding .h import at the top of your .m files, next you put other files in your project and then finally, you add system headers that use the angle brackets. 

Using this ordering ensures that you don't mask dependencies.

Wednesday 02.24.16
Posted by Luke Parham
 

What exactly is a .o again?

A .o file (object file) is the outputted machine code of your program.  The Linker’s job is to take all the .o files and stitch them together into a final executable.  The general flow is that the compiler will turn source code into assembly, the assembler will turn assembly into machine code (.o files) and the linker will take those files and put them together into the final executable.  

I was pretty fuzzy about what exactly .o files were and what the linker was in relation to the compiler.

tags: c, machine code, linker, compiler, ios
Wednesday 02.24.16
Posted by Luke Parham
 

Powered by Squarespace.