All notes
ObjectiveC

FAQ

真机测试

clang: error: no input files

The Xcode compiler keeps complaining about the error above, and when looking at the detail, you find that the file's path is wrong, although you've put the file in another path already. This gives the reason/solution: your project > target > Build Phases > Compile Sources is trying to compile a file that doesn't exist. Delete those (in red) and bingo!

Invalid context

1. Wrapping your code in calls to UIGraphicsBeginImageContext() and UIGraphicsEndImageContext().
2. Put UIImage.drawAtRect to subclassed UIView's drawRect(). http://stackoverflow.com/questions/22817574/cannot-draw-an-image

UIBezierPath *circle = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 200, 200)];  

UIGraphicsBeginImageContext(CGSizeMake(200, 200));

//this gets the graphic context
CGContextRef context = UIGraphicsGetCurrentContext();

//you can stroke and/or fill
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
CGContextSetFillColorWithColor(context, [UIColor lightGrayColor].CGColor);
[circle fill];
[circle stroke];

//now get the image from the context
UIImage *bezierImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

UIImageView *bezierImageView = [[UIImageView alloc]initWithImage:bezierImage];

GCD, Grand Central Dispatch

Ref.

Example: code block

int x = 123;
int y = 456;
 
// Block declaration and assignment
void (^aBlock)(int) = ^(int z) {
    printf("%d %d %d\n", x, y, z);
};
 
// Execute the block
aBlock(789);   // prints: 123 456 789

Categories

Serial

Concurrent

Main dispatch queue

Other

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 耗时的操作  
    dispatch_async(dispatch_get_main_queue(), ^{  
        // 更新界面  
    });  
}); 

http://stackoverflow.com/questions/7502073/loading-images-from-a-background-thread-using-blocks.

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void) {
		NSData *data0 = [NSData dataWithContentsOfURL:someURL];
		UIImage *image = [UIImage imageWithData:data0];
		
		dispatch_sync(dispatch_get_main_queue(), ^(void) {
		    UIImageView* imageView = (UIImageView*)[cell viewWithTag:100];
		    imageView.image = image;
		});
	});

http://blog.csdn.net/totogo2010/article/details/8016129.

Framework

Ref.

System Classes

Const string

In iOS, it seems const char array ends with \x13 before \0. So we must be careful when using strcmp().

const char *token = " \n\t";
char *str=strtok(buf, token);
const char *sucHead = "SucGetStudyList"; // Actually it is SucGetStudyList\x13\0.
NSLog(@"%lu", strlen(sucHead)); // 16, not 15!
// So we need to -1 from strlen() result.
if (strncmp(str,sucHead,strlen(sucHead)-1)!=0) {
	NSLog(@"Server return failure: %s.", str);
	return nil;
}

// Or we could use this instead?
const char *testp = [@"SucGetStudyList" UTF8String];
NSLog(@"%lu", strlen(testp)); // 15.

NSString

Ref. Like NSNumber and NSDecimalNumber, NSString is an immutable type, so you cannot change it after it’s been instantiated. It does, however, have a mutable counterpart called NSMutableString.

Creation

NSString *make = @"Porsche";
NSString *model = @"911";
int year = 1968;
// Watch out when the formatter has '%': "The tank is 50% full".
NSString *message = [NSString stringWithFormat:@"That's a %@ %@ from %d!", make, model, year];
NSLog(@"%@", message);

Enumeration

for (int i=0; i<[make length]; i++) {
	// unichar, which is a typedef for unsigned short, Unicode demical.
	unichar letter = [make characterAtIndex:i];
	NSLog(@"%d: %hu", i, letter);
}

Comparison

NSString *car = @"Porsche Boxster";
if ([car isEqualToString:@"Porsche Boxster"]) {
    NSLog(@"That car is a Porsche Boxster");
}
if ([car hasPrefix:@"Porsche"]) {
    NSLog(@"That car is a Porsche of some sort");
}
if ([car hasSuffix:@"Carrera"]) {
    // This won't execute
    NSLog(@"That car is a Carrera");
}

// Compare.
NSString *otherCar = @"Ferrari";
// Use caseInsensitiveCompare for case-insensitive.
NSComparisonResult result = [car compare:otherCar];
if (result == NSOrderedAscending) {
	NSLog(@"The letter 'P' comes before 'F'");
} else if (result == NSOrderedSame) {
	NSLog(@"We're comparing the same string");
} else if (result == NSOrderedDescending) {
	NSLog(@"The letter 'P' comes after 'F'");
}

Combination

NSString *make = @"Ferrari";
NSString *model = @"458 Spider";
NSString *car = [make stringByAppendingString:model];
NSLog(@"%@", car);        // Ferrari458 Spider
car = [make stringByAppendingFormat:@" %@", model];
NSLog(@"%@", car);        // Ferrari 458 Spider (note the space)

Search

NSString *car = @"Maserati GranCabrio";
NSRange searchResult = [car rangeOfString:@"Cabrio"];
if (searchResult.location == NSNotFound) {
    NSLog(@"Search string was not found");
} else {
    NSLog(@"'Cabrio' starts at index %lu and is %lu characters long",
          searchResult.location,        // 13
          searchResult.length);         // 6
}

Subdivision

NSString *car = @"Maserati GranTurismo";
NSLog(@"%@", [car substringToIndex:8]);               // Maserati
NSLog(@"%@", [car substringFromIndex:9]);             // GranTurismo
NSRange range = NSMakeRange(9, 4);
NSLog(@"%@", [car substringWithRange:range]);         // Gran
// Split into array.
NSString *models = @"Porsche,Ferrari,Maserati";
NSArray *modelsAsArray = [models componentsSeparatedByString:@","];
NSLog(@"%@", [modelsAsArray objectAtIndex:1]);        // Ferrari

Replacement

NSString *elise = @"Lotus Elise";
NSRange range = NSMakeRange(6, 5);
NSString *exige = [elise stringByReplacingCharactersInRange:range withString:@"Exige"];
NSLog(@"%@", exige);          // Lotus Exige
NSString *evora = [exige stringByReplacingOccurrencesOfString:@"Exige" withString:@"Evora"];
NSLog(@"%@", evora);          // Lotus Evora

Change case

NSString *car = @"lotUs beSpoKE";
NSLog(@"%@", [car lowercaseString]);      // lotus bespoke
NSLog(@"%@", [car uppercaseString]);      // LOTUS BESPOKE
NSLog(@"%@", [car capitalizedString]);    // Lotus Bespoke

Numerical conversion

NSString *year = @"2012";
BOOL asBool = [year boolValue];
int asInt = [year intValue];
NSInteger asInteger = [year integerValue];
long long asLongLong = [year longLongValue];
float asFloat = [year floatValue];
double asDouble = [year doubleValue];

Other issues

Split string

Ref.

NSString *a = [[NSString alloc] initWithString : @"冬瓜,西瓜,火龙果,大头,小狗" ];
NSArray *b = [a componentsSeparatedByString:@","];
NSString *a3 = [b objectAtIndex:2];
NSLog(@"\n  b的第三个东东是: %@", a3);
[a release];
// Since b is not allocated here, so we shouldn't write the following:
[b release];

Convert UTF-8 encoded NSData to NSString

Ref.

// If the data is not null-terminated, you should use
NSString* newStr = [[NSString alloc] initWithData:theData encoding:NSUTF8StringEncoding];
// If the data is null-terminated, you should instead use -stringWithUTF8String: to avoid the extra \0 at the end.
NSString* newStr = [NSString stringWithUTF8String:[theData bytes]];

See the Reference.

// Assuming: NSString *filename has content: "/path/to/file.ext".
// lastPathComponent will extract "file.ext",
// and stringByDeletingPathExtension will further remove ".ext".
// So theFileName becomes "file".
NSString* theFileName = [[filename lastPathComponent] stringByDeletingPathExtension];

NSMutableString

Creation

NSMutableString *car = [NSMutableString stringWithString:@"Porsche 911"];
[car setString:@"Porsche Boxster"];

Expansion

NSMutableString *car = [NSMutableString stringWithCapacity:20];
NSString *model = @"458 Spider";
[car setString:@"Ferrari"];
[car appendString:model];
NSLog(@"%@", car);                    // Ferrari458 Spider
[car setString:@"Ferrari"];
[car appendFormat:@" %@", model];
NSLog(@"%@", car);                    // Ferrari 458 Spider
[car setString:@"Ferrari Spider"];
[car insertString:@"458 " atIndex:8];
NSLog(@"%@", car);                    // Ferrari 458 Spider

Replace/delete substr

NSMutableString *car = [NSMutableString stringWithCapacity:20];
[car setString:@"Lotus Elise"];
[car replaceCharactersInRange:NSMakeRange(6, 5) withString:@"Exige"];
NSLog(@"%@", car);                               // Lotus Exige
[car deleteCharactersInRange:NSMakeRange(5, 6)];
NSLog(@"%@", car);                               // Lotus

Other issues

When to use mutable version?

// An anti-pattern. DO NOT DO THIS. EVER.
NSString *indices = @"";
for (int i=0; i<1000; i++) {
    indices = [indices stringByAppendingFormat:@"%d", i];
}
// This is good.
NSMutableString *indices = [NSMutableString stringWithCapacity:1];
for (int i=0; i<1000; i++) {
    [indices appendFormat:@"%d", i];
}

NSArray

Ref.

// Creation.
NSArray *germanMakes = @[@"Mercedes-Benz", @"BMW", @"Porsche", @"Opel", @"Volkswagen", @"Audi"];
NSArray *ukMakes = [NSArray arrayWithObjects:@"Aston Martin", @"Lotus", @"Jaguar", @"Bentley", nil]; // Note the last nil.
// Created from string.
NSArray *ukMakes = @[@"Aston Martin", @"Lotus", @"Jaguar", @"Bentley"];
NSLog(@"%@", [ukMakes componentsJoinedByString:@", "]);

// Index.
// The following are the same.
NSLog(@"First german make: %@", germanMakes[0]); // This is newer.
NSLog(@"First U.K. make: %@", [ukMakes objectAtIndex:0]);

// Enumeration.
// With fast-enumeration
for (NSString *item in germanMakes) {
    NSLog(@"%@", item);
}
// With a traditional for loop
for (int i=0; i<[germanMakes count]; i++) {
    NSLog(@"%d: %@", i, germanMakes[i]);
}
// Block.
[germanMakes enumerateObjectsUsingBlock:
	^(id obj, NSUInteger idx, BOOL *stop) {
		NSLog(@"%ld: %@", idx, obj);
	}
];

// Comparison.
NSArray *germanMakes = @[@"Mercedes-Benz", @"BMW", @"Porsche", @"Opel", @"Volkswagen", @"Audi"];
NSArray *sameGermanMakes = [NSArray arrayWithObjects:@"Mercedes-Benz", @"BMW", @"Porsche", @"Opel", @"Volkswagen", @"Audi", nil];
// isEqualToArray: Returns YES when both arrays have the same number of elements and every pair pass an isEqual: comparison.
if ([germanMakes isEqualToArray:sameGermanMakes]) {
    NSLog(@"Oh good, literal arrays are the same as NSArrays");
}

// Member checking.
// BOOL checking
if ([germanMakes containsObject:@"BMW"]) {
    NSLog(@"BMW is a German auto maker");
}
// Index checking
NSUInteger index = [germanMakes indexOfObject:@"BMW"];
if (index == NSNotFound) {
    NSLog(@"Well that's not quite right...");
} else {
    NSLog(@"BMW is a German auto maker and is at index %ld", index);
}

// Sorting.
NSArray *sortedMakes = [germanMakes sortedArrayUsingComparator:
	^NSComparisonResult(id obj1, id obj2) {
		if ([obj1 length] < [obj2 length]) {
			return NSOrderedAscending;
		} else if ([obj1 length] > [obj2 length]) {
			return NSOrderedDescending;
		} else {
			return NSOrderedSame;
		}
}];
NSLog(@"%@", sortedMakes);

// Filtering.
NSArray *germanMakes = @[@"Mercedes-Benz", @"BMW", @"Porsche", @"Opel", @"Volkswagen", @"Audi"];
NSPredicate *beforeL = [NSPredicate predicateWithBlock:
	^BOOL(id evaluatedObject, NSDictionary *bindings) {
		NSComparisonResult result = [@"L" compare:evaluatedObject];
		if (result == NSOrderedDescending) {
			return YES;
		} else {
			return NO;
		}
	}];
NSArray *makesBeforeL = [germanMakes filteredArrayUsingPredicate:beforeL];
NSLog(@"%@", makesBeforeL);    // BMW, Audi

// Subdividing.
NSArray *lastTwo = [germanMakes subarrayWithRange:NSMakeRange(4, 2)];
NSLog(@"%@", lastTwo);    // Volkswagen, Audi

// Combination.
NSArray *allMakes = [germanMakes arrayByAddingObjectsFromArray:ukMakes];
NSLog(@"%@", allMakes);

NSMutableArray

// Creation.
NSMutableArray *brokenCars = [NSMutableArray arrayWithObjects: @"Audi A6", @"BMW Z3", @"Audi Quattro", @"Audi TT", nil];
NSMutableArray *anArray = [NSMutableArray array];
// Is same as
NSMutableArray *anArray = [[NSMutableArray alloc] init];

// Add/Remove objs.
[brokenCars addObject:@"BMW F25"];
NSLog(@"%@", brokenCars);       // BMW F25 added to end
[brokenCars removeLastObject];
NSLog(@"%@", brokenCars);       // BMW F25 removed from end
// Add BMW F25 to front
[brokenCars insertObject:@"BMW F25" atIndex:0];
// Remove BMW F25 from front
[brokenCars removeObjectAtIndex:0];
// Remove Audi Quattro
[brokenCars removeObject:@"Audi Quattro"];
// Change second item to Audi Q5
[brokenCars replaceObjectAtIndex:1 withObject:@"Audi Q5"];

// Sorting
NSDictionary *car1 = @{
    @"make": @"Volkswagen",
    @"model": @"Golf",
    @"price": [NSDecimalNumber decimalNumberWithString:@"18750.00"]
};
NSDictionary *car2 = @{
    @"make": @"Volkswagen",
    @"model": @"Eos",
    @"price": [NSDecimalNumber decimalNumberWithString:@"35820.00"]
};
NSDictionary *car3 = @{
    @"make": @"Volkswagen",
    @"model": @"Jetta A5",
    @"price": [NSDecimalNumber decimalNumberWithString:@"16675.00"]
};
NSDictionary *car4 = @{
    @"make": @"Volkswagen",
    @"model": @"Jetta A4",
    @"price": [NSDecimalNumber decimalNumberWithString:@"16675.00"]
};
NSMutableArray *cars = [NSMutableArray arrayWithObjects: car1, car2, car3, car4, nil];

NSSortDescriptor *priceDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"price" ascending:YES selector:@selector(compare:)];
NSSortDescriptor *modelDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"model" ascending:YES selector:@selector(caseInsensitiveCompare:)];

NSArray *descriptors = @[priceDescriptor, modelDescriptor];
[cars sortUsingDescriptors:descriptors];
NSLog(@"%@", cars);    // car4, car3, car1, car2

Objective C

History

Objective C is derived from smallTalk, which was a beginner of event-based programming.

Instance variable and property

Ref.

Description method

Info.plist

Property list. The file is naed AppName-Info.plist under project.

Block object

A block object is actually called lambda function in other languages such as lisp and c.

int (^oneFrom)(int); // Declaration.

// Now comes the definition. 
oneFrom = ^(int anInt) {
    return anInt - 1;
};

printf("%d\n", oneFrom(10)); // Use it.

// To trasfer the variable into block, declare it with __block.
__block BOOL found = NO;
NSSet *aSet = [NSSet setWithObjects: @"Alpha", @"Beta", @"Gamma", @"X", nil];
NSString *string = @"gamma";
 
[aSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
    if ([obj localizedCaseInsensitiveCompare:string] == NSOrderedSame) {
        *stop = YES;
        found = YES;
    }
}];
 
// At this point, found == YES
Objective-C runtime also defines a block type: NSComparator for comparison tasks.

Target-action

The way to send/receive events around.

Memory

ARC is short for Automatic Reference Counting. In order to keep memory efficient, use

@autoreleasepool {
	...
}

IOS memory warning. References:

Talk about didReceiveMemoryWarning.

StoryBoard

The story board is the main entrance to an IOS application. Is it possible to run an IOS app without any story board?

Fixed size of main view controller?

You may be supprised that the main view displaying your image keeps a fixed small size, and you find no way to make your image cover the entire screen. The constraint is no where but in the storyboard. Follow the steps to set your view controller free, at least in size (in XCode 7):

  1. Choose Main.storyboard, and make sure the utilities panel on the right is shown.
  2. Click on the main viewController.
  3. Go to the forth tab on the utilities panel. Change the size to select Freeform. You can also check with the size in the fifth tab.
  4. Re-run your app and hola!
The reference here.

Images

Release bitmap

CGImageRelease ( CGImageRef image );

Save bitmap image

UIImage* image = [[UIImage alloc] initWithCGImage:imageRef]; NSData* imageData = UIImagePNGRepresentation(image); [imageData writeToFile:filePath atomically:YES];

Why CGContextDrawImage draws an flipped image?

Quartz2D (Mac OS X) uses a different coordinate system which sets its origin at the lower left corner instead of the upper left, thus the image drawn is flipped. In iOS the coordinate system has the origin at upper left. See the apple page for reference. You may want to use the simpler way to draw:

UIImage *image = [UIImage imageNamed:@"a.png"];
[image drawInRect:CGRectMake(0, 0, rect.width, rect.height)];

The recommended way is to use Current Transform Matrix (CTM) to do the flipping back:

CGContextTranslateCTM(context, 0, image.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
To convert back,
CGContextScaleCTM(ctx, 1.0, -1.0);
CGContextTranslateCTM(ctx, 0, -imageRect.size.height);

The reference page also has a discussion that the whole context is affected by the CTM. A good suggestion is to use CGContextSaveGState() and CGContextRestoreGState() to save and restore the CTM.

Some useful functions

  1. UIGraphicsPushContext/UIGraphicsPopContext(context). Push context onto a stack of CGContextRefs and the following drawings will be performed on the context.
  2. CGContextSaveGState/CGContextRestoreGState(context). Save the current graphics state onto the stack of graphics state maintained by context, e.g. save the CTM.
[[UIColor redColor] setStroke];
CGContextSaveGState(UIGraphicsGetCurrentContext());
[[UIColor blackColor] setStroke];
CGContextRestoreGState(UIGraphicsGetCurrentContext());
UIRectFill(CGRectMake(10, 10, 100, 100)); // Red

Constructor

Example code (Reference):

-(id)init 
{
	self = [super init];
	if (self) 
	{
		// superclass successfully initialized, further 
		// initialization happens here ...
	}
	return self;
}
+(void)initialize
{
	// This is the class static function called at least
	// once when the app starts. Use this to initialize
	// those static variables used across all instances
	// of the class.
}

Destructor

Here is the reference. Note that it is not needed to call super.dealloc, otherwise there could be compiler error.

-(void)dealloc {
	//cleanup code
	// [super dealloc]; // Shouldn't call super.dealloc since ARC.
}

iOS memory query

Example code (Reference):

void calcMem(double *memFree, double *memTotal=0)
{
	mach_port_t host_port;
	mach_msg_type_number_t host_size;
	vm_size_t pagesize;
	
	host_port = mach_host_self();
	host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
	host_page_size(host_port, &pagesize);
	
	vm_statistics_data_t vm_stat;
	
	if (host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size) != KERN_SUCCESS) {
	    cerr<<"Error. Failed to fetch vm statistics!"<

Another reference on GNU. It's written for GNU Mach, a microsystem.

Properties

  • nonatomic
  • copy
  • assign
  • retain
  • strong
  • weak

Simulator