if (CGRectContainsPoint(self.menuView.frame, point)) {
point = [self.view convertPoint:point toView:self.menuView];
selectedView = [[self.itemViews filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(RNMenuItemView *itemView, NSDictionary *bindings) {
return CGRectContainsPoint(itemView.frame, point);
}]] lastObject];
}
模式匹配
NSString *someRegexp = ...;
NSPredicate *myTest = [NSPredicate predicateWithFormat:@"SELF
MATCHES %@", someRegexp];
if ([myTest evaluateWithObject:
testString]){
//Matches
}
创建模板谓词
People *p1 = [People new];
p1.firstName = @"12345";
People *p2 = [People new];
p2.firstName = @"123456";
People *p3 = [People new];
p3.firstName = @"123";
NSArray *array = @[p1, p3, p2];
// 设置一个模板谓词
NSPredicate *_predicate = [NSPredicate
predicateWithBlock:^BOOL(People *people, NSDictionary *bindings) {
return [people.firstName length] >
[[bindings valueForKey:@"length"] intValue];
}];
NSDictionary *bindings =
@{@"length" : @3};
NSIndexSet *indexSet = [array
indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
return [_predicate evaluateWithObject:obj
substitutionVariables:bindings];
}];
对象有一个isa指针指向它的class, class有一个isa指针指向meta class。如果想查看,添加,修改类方法和类级别的信息时,就需要使用meta class。示例代码如下:
IMP myIMP = imp_implementationWithBlock(^(id _self, NSString *string) {
NSLog(@"Hello %@", string);
});
Class cls = objc_getMetaClass("NSString");
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
class_addMethod(cls, @selector(test:), myIMP, "v@:@");
[[NSString class] performSelector:@selector(test:) withObject:@"first"];
#pragma clang diagnostic pop
为了获取实例方法,可以在类对象上使用class_copyMethodList(Class cls, unsigned int *outCount)函数。函数将返回一个包含该类定义的(不会搜索超类)所有实例方法的数组。如果需要搜索父类,可以使用class_getInstanceMethod(Class aClass, SEL aSelector)。
你也可以在meta class上调用class_copyMethodList(Class cls, unsigned int *outCount)函数来获取定义的所有类方法。
在Build项目时,自动启用静态分析器。
-
动态方法解析和消息转发
#import "Forwarder.h"
#import <objc/Object.h>
@interface Man : NSObject
- (void)method;
@end
@implementation Man
- (void)method {
NSLog(@"forwardingMethod");
}
@end
@interface Forwarder()
@property (nonatomic, strong)
Man *man;
@end
@implementation Forwarder
@dynamic recipent;
- (void)test {
/* 动态方法解析 */
[self setRecipent:@"water"];
NSLog(@"%@", self.recipent);
/* 消息转发 */
Man *man = [Man new];
self.man = man;
[self performSelector:@selector(method)];
}
- (void)setRecipent:(NSString
*)recipent {
_recipent = recipent;
}
- (NSString *)recipent {
return _recipent;
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(setRecipent:)) {
Method method = class_getInstanceMethod(self.class,
@selector(setRecipent:));
IMP imp = method_getImplementation(method);
class_addMethod([self class], sel, imp, method_getTypeEncoding(method));
}
if (sel == @selector(recipent)) {
Method method = class_getInstanceMethod(self.class,
@selector(recipent));
IMP imp = method_getImplementation(method);
class_addMethod([self class], sel, imp, method_getTypeEncoding(method));
}
return [super resolveInstanceMethod:sel];
}
/* 必须覆盖此方法 */
- (NSMethodSignature
*)methodSignatureForSelector:(SEL)aSelector {
return [self.man.class instanceMethodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation
*)anInvocation {
SEL selector = anInvocation.selector;
if ([self.man respondsToSelector:selector]) {
[anInvocation invokeWithTarget:self.man];
} else {
[self doesNotRecognizeSelector:selector];
}
}
@end
-
dispatch_async和dispatch_sync的区别:http://t.cn/8slRGAM
-
version编号与build编号:http://stackoverflow.com/questions/6851660/version-vs-build-in-xcode-4
-
NSRUNLoop。点击按钮下载,
- (IBAction)downloadButtonPressed:(id)sender
{
NSData *date = [self startLoadingImage];
UIImage *image = [UIImage imageWithData:date];
self.imageView.image = image;
}
- (NSData *)
startLoadingImage {
__block BOOL stillDownloading = YES;
__block __strong id responde = nil;
NSURLRequest* req = [NSURLRequest requestWithURL:[NSURL
URLWithString:@"http://img0.bdstatic.com/static/common/widget/search_box_search/logo/logo_3b6de4c.png"]];
[NSURLConnection sendAsynchronousRequest:req queue:[NSOperationQueue
new] completionHandler:^(NSURLResponse* resp, NSData* data, NSError* error) {
stillDownloading = NO;
responde = data;
}];
int allowTime = 30;
NSDate* timeoutDate = [NSDate dateWithTimeIntervalSinceNow:allowTime];
// 图片在另一队列中下载,但是当前的线程也不会退出,不同于使用for循环轮询。
while (stillDownloading &&
([timeoutDate timeIntervalSinceNow] > 0)) { // 当下载完成或或可用时间已经用完,退出循环
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.01,
YES);
}
stillDownloading = NO;
if ([timeoutDate timeIntervalSinceNow] < 0)
{ // 超时报失败
NSLog(@"Failure");
}
return responde;
}
-
等待若干秒执行后续任务
static const NSInteger
kRunLoopSamplingInterval = 0.01; // 为什么是0.01而不是0,解释:
-(void)waitForTimeout:(NSTimeInterval)timeout
{
NSDate* waitEndDate = [NSDate dateWithTimeIntervalSinceNow:timeout];
while ([waitEndDate timeIntervalSinceNow]>0)
{
CFRunLoopRunInMode(kCFRunLoopDefaultMode, kRunLoopSamplingInterval,
YES);
}
}
-
获取设备的颜色
UIDevice *device = [UIDevice currentDevice];
SEL selector = NSSelectorFromString(@"deviceInfoForKey:");
if (![device respondsToSelector:selector]) {
selector = NSSelectorFromString(@"_deviceInfoForKey:");
}
if ([device respondsToSelector:selector]) {
NSLog(@"DeviceColor: %@ DeviceEnclosureColor: %@", [device performSelector:selector withObject:@"DeviceColor"], [device performSelector:selector withObject:@"DeviceEnclosureColor"]);
}
-
版本号管理策略 from:http://www.cnblogs.com/sdjxcolin/archive/2007/07/02/803376.html
一、GNU 风格的版本号管理策略:
1.项目初版本时,版本号可以为 0.1 或 0.1.0, 也可以为 1.0 或 1.0.0,如果你为人很低调,我想你会选择那个主版本号为 0 的方式;
2.当项目在进行了局部修改或 bug 修正时,主版本号和子版本号都不变,修正版本号加 1;
3. 当项目在原有的基础上增加了部分功能时,主版本号不变,子版本号加 1,修正版本号复位为 0,因而可以被忽略掉;
4.当项目在进行了重大修改或局部修正累积较多,而导致项目整体发生全局变化时,主版本号加 1;
5.另外,编译版本号一般是编译器在编译过程中自动生成的,我们只定义其格式,并不进行人为控制。
对于用户来说,如果某个软件的主版本号进行了升级,用户还想继续那个软件,则发行软件的公司一般要对用户收取升级费用;而如果子版本号或修正版本号发生了升级,一般来说是免费的。
- 模拟app升级:https://developer.apple.com/library/ios/technotes/tn2285/_index.html
- 输入的字符必须包含一个数字和字母
- (BOOL)isValidateString:(NSString
*)string {
BOOL isExistDigit = [string rangeOfCharacterFromSet:[NSCharacterSet
decimalDigitCharacterSet]].location != NSNotFound;
BOOL isExistLetter = [string rangeOfCharacterFromSet:[NSCharacterSet
letterCharacterSet]].location != NSNotFound;
return isExistDigit && isExistLetter;
}
- 输入的字符是否全是数字
NSCharacterSet
*_NumericOnly = [NSCharacterSet decimalDigitCharacterSet];
NSCharacterSet *myStringSet
= [NSCharacterSet characterSetWithCharactersInString:mystring];
if ([_NumericOnly
isSupersetOfSet: myStringSet]) {
NSLog(@"String has only
numbers");
}
- 设置依赖与完成块
[stage2RequestOperation
addDependency:stage1RequestOperation];
[MobileHttpClient sharedHttpClient] enqueueBatchOfHTTPRequestOperations:<#(NSArray *)#> progressBlock:<#^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations)progressBlock#> completionBlock:<#^(NSArray *operations)completionBlock#>
- 请设想和设计框架的public的API,并指出大概需要如何做、需要注意一些什么方面,来使别人容易地使用你的框架。
-
如果模拟器偶尔会出现无法启动,Xcode窗口顶部显示Attaching to<app name>,解决办法:
确保在/etc/hosts文件中,localhost映射到了127.0.0.1。
在终端执行命令:
$ grep localhost /etc/hosts
如果grep没有输出127.0.0.1,继续在终端输入下面的命令,将localhost映射加入到hosts文件:
$ sudo -i
# echo "127.0.0.1 localhost" >> /etc/hosts
-
你实现过多线程的Core Data么?NSPersistentStoreCoordinator,NSManagedObjectContext和NSManagedObject中的哪些需要在线程中创建或者传递?你是用什么样的策略来实现的?
- 有两种形式的对象ID。当一个NSManagedObject对象初次创建,Core Data会分配一个临时ID;直到对象被保存到持久化存储区,Core Data将会为对象分配一个永久ID。你可以检查ID是否是临时的:
- XCode快捷键:Command+零 打开或隐藏导航面板;Shift+Command+字母O 搜索文件;Command+字母L 定位代码行。
- KVC集合操作:
Core Data并发基本规则:
1. 一个程序中只需要一个NSPersistentStoreCoordinator。你不需要在每个线程单独创建一个。
2. 为每个线程中创建单独一个NSManagedObjectContext。
3. 不允许在线程之间传递NSManagedObject对象。
4. 你应该通过-objectID获取对象ID,然后传递给其他线程。
更多规则:
1. 在获得对象ID之前确保对象已保存到存储区。在保存之前,对象只是临时存在于特定的上下文中,所以你不能使用其他线程中访问,因为每个线程使用的上下文是不一样的,参照基本规则2。
2. 如果NSManagedObject被多个线程中修改了,当心合并策略。
3. NSManagedObjectContext的mergeChangesFromContextDidSaveNotification对你会有帮助。
BOOL isTemporary = [[managedObject objectID] isTemporaryID];
NSNumber *average = [@[@1, @2, @3]
valueForKeyPath:@"@avg.self"]; // 2
NSNumber *salaryOverhead = [anEmployee valueForKeyPath:@"department.employees.@sum.salary"];
- NSOrderedSet与子类NSMutableOrderedSet定义了针对有序对象集合的编程接口, 当既要保持元素次序,也要考虑检查元素成员关系(containsObject)时的性能,NSOrderSet则可以作为数组的一种替代方案。NSOrderSet在测试集合元素的成员关系要比测试数组元素成员关系时更快。
// 4, 6, 1, 7, 3
NSOrderedSet *orderedSet = [NSOrderedSet
orderedSetWithArray:@[@4, @6, @1, @7, @3]];
// 7, 3, 6, 4, 1
NSSet *set = [NSSet setWithArray:@[@4, @6, @1, @7, @3]];
-
文件路径
NSString *path = [[NSBundle bundleForClass:[SGCViewController class]].resourcePath stringByAppendingPathComponent:@"cde.png"];
-
覆盖文件,使用destinationFile文件覆盖sourceFile文件,覆盖之后sourceFile文件被删除,destinationFile的文件名被自动修改为sourceFile的命名
[self replaceStore:sourceFileURL withStore:destinationFileURL];
-
检查指定文件是否存在
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *documentsPath =
[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *filePath = [documentsPath stringByAppendingPathComponent:@"file.txt"];
BOOL fileExists = [fileManager fileExistsAtPath:filePath];
递归枚举指定目录内的文件
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *bundleURL = [[NSBundle mainBundle] bundleURL];
NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtURL:bundleURL
includingPropertiesForKeys:@[NSURLNameKey, NSURLIsDirectoryKey]
options:NSDirectoryEnumerationSkipsHiddenFiles
errorHandler:^BOOL(NSURL *url, NSError *error) {
NSLog(@"[Error] %@ (%@)", error, url);
return NO;
}];
NSMutableArray *mutableFileURLs = [NSMutableArray array];
for (NSURL *fileURL in enumerator) {
NSString *filename;
[fileURL getResourceValue:&filename forKey:NSURLNameKey error:nil];
NSNumber *isDirectory;
[fileURL getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil];
// Skip directories with '_' prefix, for example
if ([filename hasPrefix:@"_"] && [isDirectory boolValue]) {
[enumerator skipDescendants];
continue;
}
if (![isDirectory boolValue]) {
[mutableFileURLs addObject:fileURL];
}
}
列出指定目录内的所有文件
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *bundleURL = [[NSBundle mainBundle] bundleURL];
NSArray *contents = [fileManager contentsOfDirectoryAtURL:bundleURL
includingPropertiesForKeys:@[]
options:NSDirectoryEnumerationSkipsHiddenFiles
error:nil];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"pathExtension ENDSWITH '.png'"];
for (NSString *path in [contents filteredArrayUsingPredicate:predicate]) {
// Enumerate each .png file in directory
}
创建目录
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *imagesPath = [documentsPath stringByAppendingPathComponent:@"images"];
if (![fileManager fileExistsAtPath:imagesPath]) {
[fileManager createDirectoryAtPath:imagesPath
withIntermediateDirectories:NO
attributes:nil
error:nil];
}
删除指定文件
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *filePath = [documentsPath stringByAppendingPathComponent:@"image.png"];
NSError *error = nil;
if (![fileManager removeItemAtPath:filePath error:&error]) {
NSLog(@"[Error] %@ (%@)", error, filePath);
}
阻止对PDF文件的删除操作
// 对NSFileManager实例设置代理必须初始化一个新的唯一实例,不要使用defaultManger方法生成的实例。
NSFileManager *fileManager = [[NSFileManager alloc] init];
fileManager.delegate = delegate;
NSURL *bundleURL = [[NSBundle mainBundle] bundleURL];
NSArray *contents = [fileManager contentsOfDirectoryAtURL:bundleURL
includingPropertiesForKeys:@[]
options:NSDirectoryEnumerationSkipsHiddenFiles
error:nil];
for (NSString *filePath in contents) {
[fileManager removeItemAtPath:filePath error:nil];
}
代理实现:
- (BOOL)fileManager:(NSFileManager *)fileManager shouldRemoveItemAtURL:(NSURL *)URL {
return ![[[URL lastPathComponent] pathExtension] isEqualToString:@"pdf"];
}
NSFileManager代理方法包括
Ø -fileManager:shouldMoveItemAtURL:toURL:
Ø -fileManager:shouldCopyItemAtURL:toURL:
Ø -fileManager:shouldRemoveItemAtURL:
Ø -fileManager:shouldLinkItemAtURL:toURL:
- NSExpression内建统计、算术运算、逻辑运算方法。 它的功能异常强大,尤其配合Core Data使用,当你的数据库中有许多关于Person模型的数据,你只想得到1990之后出生的男性的平均身高,最好的做法是使用NSExpression,它不会将所有数据载入内存进行计算。此处代码会在日后进行补充。可参见WWDC 211视频,时间点:20:18。
NSExpression *expression = [NSExpression expressionWithFormat:@"4 + 5 - 2**2"];// 9 – 2^2
id value = [expression expressionValueWithObject:nil context:nil]; // 5
NSLog(@"%@", value);
NSArray *numbers = @[@1, @2, @3, @4, @4, @5, @9, @11];
NSExpression *expression = [NSExpression expressionForFunction:@"sum:" arguments:@[[NSExpression expressionForConstantValue:numbers]]];
id value = [expression expressionValueWithObject:nil context:nil]; // 39
// 公式1
NSExpression *expr = [NSExpression expressionWithFormat: @"2 * x + 4 * y"];
float result = [[expr expressionValueWithObject:@{@"x":@2, @"y" :@2} context:nil] floatValue]; // 12
// 公式2
expression = [NSExpression expressionWithFormat:@"(%@ + %@)**2", @4, @4];
NSNumber *result = [expression expressionValueWithObject:nil context:nil]; // 64
-
NSPredicate是一个指定获取与过滤数据方式的Foundation类。它的查询语句类似于SQL的WHERE子句与正则表达式的混合体。提供了一种在待搜索容器上定义搜索逻辑的描述性自然语言接口。
NSArray *firstNames = @[ @"Alice", @"Bob", @"Charlie", @"Quentin" ];
NSArray *lastNames = @[ @"Smith", @"Jones", @"Smith", @"Alberts" ];
NSArray *ages = @[ @24, @27, @33, @31 ];
NSMutableArray *people = [NSMutableArray array];
[firstNames enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
Person *person = [[Person alloc] init];
person.firstName = firstNames[idx];
person.lastName = lastNames[idx];
person.age = ages[idx];
[people addObject:person];
}];
// BEGINSWITH、CONTAINS、ENDSWITH、LIKE、MATCHES,IN.
// 默认是大小写和变音符(diacritic)敏感,如果在方括号中加入c则表示大小写不敏感,加入d表示变音符不敏感
NSPredicate *namesBeginningWithLetterPredicate = [NSPredicate predicateWithFormat:@"(firstName BEGINSWITH[cd] $letter) OR (lastName BEGINSWITH[cd] $letter)"];
// ["Alice Smith", "Quentin Alberts"]
NSLog(@"'A' Names: %@", [people filteredArrayUsingPredicate: [namesBeginningWithLetterPredicate predicateWithSubstitutionVariables:@{@"letter": @"A"}]]);
// K表示键路径
NSPredicate *ageIs33Predicate = [NSPredicate predicateWithFormat:@"%K = %@", @"age", @33];
// ["Charlie Smith"]
NSLog(@"Age 33: %@", [people filteredArrayUsingPredicate:ageIs33Predicate]);
// 使用block方式实现过滤
NSPredicate *shortNamePredicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return [[evaluatedObject firstName] length] <= 5;
}];
// ["Alice Smith", "Bob Jones"]
NSLog(@"Short Names: %@", [people filteredArrayUsingPredicate:shortNamePredicate]);
-
__attribute__是一个对声明添加特性(attribute)的编译器指令,它允许更多的错误检查和高级优化。
该关键字的语法是__attribute__后面跟随两对圆括号。在圆括号的里面是一个以逗号分隔的属性列表。__attribute__指令被放置在函数、变量以及类型声明之后。
常用的__attribute__代码: partly from: http://t.cn/zO7OtRt
// 几乎用不到
extern void myprint(const char *format,...) __attribute__((format(printf,1,2)));
extern void myprint(int l,const char *format,...) __attribute__((format(printf,2,3)));
extern void myprint(int l,const char *format,...) __attribute__((format(printf,3,4)));
extern void exit(int) __attribute__((noreturn));
extern void abort(void) __attribute__((noreturn));
int square(int n) __attribute__((const));
void f(void) __attribute__((availability(macosx,introduced=10.4,depre cated=10.6)));
extern void die(const char *format, ...) __attribute__((noreturn, format(printf, 1, 2)));
// 1.有用的,声明已过时属性,并给出提示信息
@property (nonatomic, readwrite) NSUInteger selectedIndex __attribute__((deprecated("提示消息")));
// 2. 声明已过时属性,并给出提示消息
@property (strong) NSObject *object DEPRECATED_MSG_ATTRIBUTE("提示消息");
// 3. 声明已过时属性
@property (strong) NSObject *object DEPRECATED_ATTRIBUTE;
-
相等和恒等
如果两个对象共享一个公共的可见属性,则认为两个对象是相等的。然而,这两个对象仍然被认为是有区别的,因为两个对象各自拥有不同的标识。在编程语言中,一个对象的标识是与它相绑定的内存地址。NSObject使用isEqual:方法测试与另一个对象的相等性。在NSObject的基本实现中,相等性检查的本质是测试标识。如果两个NSObject指向的内存地址相同则被认为是相等的。
@implementation NSObject (Approximate)
- (BOOL)isEqual:(id)object {
return self == object;
}
@end
对一个诸如NSArray,NSDictioanry,NSString的容器类。相等是测试容器内的每个成员是否相等。 NSObject子类的isEqual:方法实现被期望做到以下几点:
Ø 实现一个新的isEqualTo_ClassName_方法,该方法执行有意义的值比较。
Ø 覆盖isEuqal:方法对类和对象的标识做检查。
Ø 覆盖hash方法
下面是一个NSArray可能的实现:
@implementation NSArray (Approximate)
- (BOOL)isEqualToArray:(NSArray *)array {
if (!array || [self count] != [array count]) {
return NO;
}
for (NSUInteger idx = 0; idx < [array count]; idx++) {
if (![self[idx] isEqual:array[idx]]) {
return NO;
}
}
return YES;
}
- (BOOL)isEqual:(id)object {
if (self == object) {
return YES;
}
if (![object isKindOfClass:[NSArray class]]) {
return NO;
}
return [self isEqualToArray:(NSArray *)object];
}
@end
下面是Foudation框架中NSObject子类的自定义相等性实现,以及对应的方法:
NSAttributedString
-isEqualToAttributedString:
NSData
-isEqualToData:
NSDate
-isEqualToDate:
NSDictionary
-isEqualToDictionary:
NSHashTable
-isEqualToHashTable:
NSIndexSet
-isEqualToIndexSet:
NSNumber
-isEqualToNumber:
NSOrderedSet
-isEqualToOrderedSet:
NSSet
-isEqualToSet:
NSString
-isEqualToString:
NSTimeZone
-isEqualToTimeZone:
NSValue
-isEqualToValue:
当比较上述任一类的两个实例,推荐使用高级的相等性方法而不是使用isEqual:方法。
NSString相等性的怪异表现
考虑如下代码:
NSString *a = @”Hello”;
NSString *a = @”Hello”;
BOOL wtf = (a == b); // YES
注意:正确的方式是使用-iSEqualToString:方法比较字符串对象。在任何情况下都不要使用==比较两个字符串。
那这里又发生了什么?为什么会得到正确的结果?这一切都和一种称为“字符串驻留”的优化技术有关。字符串驻留技术依靠将一份不可变字符串值拷贝复制给每个不同的值。NSString a和*b指向相同的驻留字符串值@”Hello”拷贝。注意这仅对静态定义的不可变字符串有效。有趣的是,Objective-C语言中selector名也被存储为共享字符串池中的驻留字符串。
- 哈希
哈希算法遵循的定理:
Ø 对象相等性是可交换的。
[a isEqual:b]=>[b isEqual:a]
Ø 如果对象相等,那么它们的hash值必须也是相等的。
[a isEqual:b]=>([a hash] == [b hash])
Ø 然而,反之则不成立:两个对象的hash值相等不一定两个对象是相等的。
([a hash] == [b hash]) ﹁=>[a isEqual:b]
子类对-isEqual:和hash方法实现示例:
Person.h
@interface Person
@property NSString *name;
@property NSDate *birthday;
- (BOOL)isEqualToPerson:(Person *)person;
@end
Person.m
@implementation Person
- (BOOL)isEqualToPerson:(Person *)person {
if (!person) {
return NO;
}
BOOL haveEqualNames = (!self.name && !person.name) ||[self.name isEqualToString:person.name];
BOOL haveEqualBirthdays = (!self.birthday && !person.birthday) || [self.birthday isEqualToDate:person.birthday];
return haveEqualNames && haveEqualBirthdays;
}
#pragma mark - NSObject
- (BOOL)isEqual:(id)object {
if (self == object) {
return YES;
}
if (![object isKindOfClass:[Person class]]) {
return NO;
}
return [self isEqualToPerson:(Person *)object];
}
- (NSUInteger)hash {
return [self.name hash] ^ [self.birthday hash];
}
@end
-
Objective-C语言定义了多个字面值表示空值。在C语言中,用0表示基本类型变量的空值,NULL表示指针的空值。在Objective-C,nil表示对象指针的空值;虽然语义上nil和NULL不同,但技术上两者是相等的。在框架级,Foundation又定义了NSNull,NSNull有一个类方法+null。该方法返回一个单例的NSNull对象。NSNull与前两个字面值NULL和nil的不同在于,NSNull是一个实际的对象而不是0。NSNull的使用贯穿于Foundation等诸多框架中,它用于绕过NSArray和NSDctionary不能包含nil值的限制。可以认为NSNull是对Null和nil值的包装以让这些值可用于容器类中。下面代码说明了可将NSNull加入NSDictioanry:
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionary];
mutableDictionary[@"someKey"] = [NSNull null];
NSLog(@"Keys: %@", [mutableDictionary allKeys]); // @[@"someKey"]
此外,在Foundation/NSObjCRuntime.h文件中,还定义了一个Nil表示类指针的空值;
符号
值
含义
NULL
(void *)0
C语言指针空值
nil
(id)0
对象指针空值
Nil
(Class)0
类指针空值
NSNull
[NSNull null]
一个表示空值的对象
extern BOOL isNull(id value);
extern BOOL isNull(id value)
{
if (!value) return YES;
if ([value isKindOfClass:[NSNull class]]) return YES;
return NO;
}
-
NSError对象用于表示Cocoa程序的运行时错误。NSError有3个核心属性:错误域domain、错误码code、错误信息字典userInfo。NSError可以被归档和复制,NSError不是抽象类但你可以通过继承扩展NSError。一般你通过生产者或消费者的身份接触NSError。以消费者身份,你将获得一个包含错误信息的NSError对象。
以消费者身份获得NSError对象的情形一:当你调用Cocoa/Cocoa Touch框架的某些创建文档、写文件、从URL加载数据等方法时,都会包含一个对NSError对象的引用。如果调用的方法执行出错,则你将获得一个含有错误信息的NSError对象。如下代码所示:
NSError *error = nil;
BOOL success = [[NSFileManager defaultManager] moveItemAtPath:@"/path/to/target"
toPath:@"/path/to/destination"
error:&error];
if (!success) { // 应该总是在使用NSError对象之前先检查返回值是nil或NO。而不是先if(error)
NSLog(@"%@", error);
}
第二种获得NSError对象的情形:某些类的代理方法包含对NSError对象的引用:
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error;
最后一种以消费者身份获得NSError对象的情况常见于最新的Foundation API中,如NSURLSession:
NSURL *URL = [NSURL URLWithString:@"http://example.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[[session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(@"%@", error);
} else {
// ...
}
}] resume];
作为生产者身份,NSError对象则由你自己来创建。如下代码展示了KVC属性验证方法,
-(BOOL)validateName:(id *)ioValue error:(NSError * __autoreleasing *)outError{
// name不能为nil并且长度不能小于2个字符.
if ((*ioValue == nil) || ([(NSString *)*ioValue length] < 2)) {
if (outError != NULL) { // 赋值之前,须先检查outError不等于NULL。如果为NULL,*outError保存不了任何赋值给它的NSError对象。如果为NULL也表明用户不想知道任何关于错误的细节。
/** 构造NSError对象 **/
NSString *errorString = NSLocalizedString( @"人名不少于两个字符", nil);
NSDictionary *userInfoDict = @{ NSLocalizedDescriptionKey : errorString };
*outError = [[NSError alloc] initWithDomain:PERSON_ERROR_DOMAIN
code:PERSON_INVALID_NAME_CODE
userInfo:userInfoDict];
}
return NO;
}
return YES;
}
构造NSError对象需要错误域domain、错误码code、userInfo字典。每一个NSError对象都有一个userInfo字典,用于存放比domain和code更详细的错误信息。相对于只使用错误码或错误字符串来表示错误信息。NSError一次性封装了多个错误信息。iOS中NSError常用的三个只读属性和键:
➢ localizedDescription (NSLocalizedDescriptionKey): 错误的本地化描述。
➢ localizedFailureReason (NSLocalizedFailureReasonErrorKey): 一个本地化的对错误原因的解释。
➢ localizedRecoverySuggestion (NSLocalizedRecoverySuggestionErrorKey): 本地化的错误的恢复建议。
错误域domain类似于一个文件夹或名字空间,用于对错误码进行分类。错误域可以防止不同子系统内数值相等的错误码出现冲突。你可以在程序或框架中创建自己的错误域。推荐的错误域形式:com.company.framework_or_app.ErrorDomain。错误域创建示例代码:
extern NSString * const kAppErrorDomain; // 在AppDelegate.h文件
NSString *const kAppErrorDomain = @"com.rednoble.mapper.ErrorDomain"; // 在AppDelegate.m文件
错误码code标识了一个特定错误域内的一个特定错误。错误码是一个有符号整数,它被指派为一个程序符号。你可以对NSError对象发送code消息获取错误码。一个优秀的app都应该定义自己的错误域和错误码。
构造一个NSError对象:
NSDictionary *userInfo = @{
NSLocalizedDescriptionKey: NSLocalizedString(@"Operation was unsuccessful.", nil),
NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"The operation timed out.", nil),
NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Have you tried turning it off and on again?", nil)
};
NSError *error = [NSError errorWithDomain:MapperErrorDomain
code:MapperErrorBadJSON
userInfo:userInfo];
显示错误对象:
[[[UIAlertView alloc] initWithTitle:err.localizedDescription
message:err.localizedFailureReason
delegate:self
cancelButtonTitle:@"我知道了"
otherButtonTitles:err.localizedRecoverySuggestion,nil] show];
- int val = 10; NSNumber *number = @(val);
-
struct Image{
int size;
const char *url; // 禁止在C结构体内使用对象指针
};
typedef struct Image Image;
Image image;
image.size = [oneImage[@"size"] intValue];
image.url = [oneImage[@"url"] UTF8String];
// NSValue装箱结构体
NSValue *anObj = [NSValue value:&image withObjCType:@encode(Image)];
// 拆箱
Image newImage;
[anObj getValue:&newImage];
- 属性的默认attribute值为atomic,readwrite,assign。from:http://t.cn/8kBTX4k
- 函数const char *property_getAttributes(objc_property_t property)获取类Lender的属性(property)名、属性的@encode类型字符串、以及attribute(指strong,weak,retain,copy,assign,nonatomic,readonly,getter,setter)。(Objecitve-C语言运行时介绍:http://t.cn/8kQJ5qJ 中文:http://t.cn/8kQMDuV)
#import <Foundation/Foundation.h>
#import "KivaFeed.h"
/** output:
T@"NSString",C,N,V_name
T{LOCATION=ff},N,V_locaiton
T@"NSArray",&,N,V_colors
Ti,N,V_count
Tc,N,V_success
Tc,N,GisSquare,V_square
Td,R,N,V_time
T@"KivaFeed",&,N,V_kivaFeed
T@"NSArray<FirstProtocol><SecondProtocol>",W,N,V_lenders
*/
struct LOCATION{
float latitude;
float longitude;
};
@protocol FirstProtocol @end
@protocol SecondProtocol @end
@interface Lender : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) struct LOCATION locaiton;
@property (nonatomic, retain) NSArray *colors;
@property (nonatomic, assign) NSInteger count;
@property (nonatomic, assign) BOOL success;
@property (nonatomic, assign, getter = isSquare) BOOL square;
@property (nonatomic, assign, readonly) NSTimeInterval time;
@property (nonatomic, strong) KivaFeed *kivaFeed;
@property (nonatomic, weak) NSArray<FirstProtocol, SecondProtocol> *lenders;
@end
- 使用MBProgressHUD的一些便利方法:http://t.cn/8kWRFPH
其中 +(UIView *)rootView 方法返回当前窗口根视图控制器的视图:
+(UIView*)rootView {
UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
while (topController.presentedViewController) {
topController = topController.presentedViewController;
}
return topController.view;
}
- 考虑下面代码
NSUInteger x = 1;
int r = MAX(0, x - 10);
NSLog(@"%d", r);
输出结果为-9。
具体解释:http://t.cn/8kNCq7k。简而言之,0是有符号数,x是无符号数。不要将符号数和无符号数相比较。
- 解决app进入后台再回到前后后,之前正在运行的动画被移除而停止的问题:http://t.cn/8kCJFIA
- 避免图像解压缩的时间开销:http://t.cn/8k9HRim 示例代码:http://t.cn/8kOvFIa
- 揭秘iOS证书和配置文件:http://t.cn/8kvmu7p
- iOS App目录结构:from:http://t.cn/8kKO5M0 (需要翻墙)也可参阅:http://t.cn/8kKOUVI
iOS Applicaiton目录结构图
Bundle目录(myApp.app)
此目录包含了项目中的所有资源。iOS2.1和以后的版本,iTunes不备份该目录。
访问代码:
NSBundle *bundle = [NSBundle mainBundle];
NSString *bundlePath = [bundle bundlePath];
// /var/mobile/Applications/2DD71ECB-1027-4197-AAAB-BD00A67727ED/Test.app
NSLog(@"%@", bundlePath);
NSArray *imagePaths = [bundle pathsForResourcesOfType:@"png" inDirectory:@"Demo Images"];
注意到红色文字部分, iOS使用唯一ID代替应用名标识沙盒路径,所以不要使用绝对或相对路径查找目录。
Document目录
使用该目录存放不可再生文件,如sqlite数据库。该目录的数据将被iTunes备份。
目录访问代码:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString* docDir = [paths objectAtIndex:0];
// Document目录:/var/mobile/Applications/2DD71ECB-1027-4197-AAAB-BD00A67727ED/Documents
NSLog(@"Document目录:%@ ",docDir);
Library目录
该目录不存放用户的数据文件。iOS程序没有权限使用该目录。但你可以通过在该目录创建子目录的方式存放需要备份但不向用户暴露的文件。
访问代码:
paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *libDirectory = [paths objectAtIndex:0];
// Library目录:/var/mobile/Applications/2DD71ECB-1027-4197-AAAB-BD00A67727ED/Library
NSLog(@"Library目录:%@",libDirectory);
Library/Caches目录
该目录不被iTunes备份。该目录可用于下载内容,存放数据库缓存文件等可再造的数据文件。
访问代码:
paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachesDirectory = [paths objectAtIndex:0];
// Library/Caches目录:/var/mobile/Applications/2DD71ECB-1027-4197-AAAB-BD00A67727ED/Library/Caches
NSLog(@"Library/Caches目录:%@",cachesDirectory);
Library/Preferences目录
Apple建议不要在该目录创建任何文件。记录NSUserDefault类设值的plist文件存放于此目录。
tmp目录
存放临时文件的目录,任何时候目录内的文件都可被删除。
访问代码:
NSString *tmpDirectory = NSTemporaryDirectory();
// tmp目录:/private/var/mobile/Applications/2DD71ECB-1027-4197-AAAB-BD00A67727ED/tmp/
NSLog(@"tmp目录:%@",tmpDirectory);
- stack的三种含义。from:http://www.ruanyifeng.com/blog/2013/11/stack.html?utm_campaign=Manong_Weekly_Issue_12&utm_medium=EDM&utm_source=Manong_Weekly
stack的第三种含义是存放数据的一种内存区域。程序运行的时候,需要内存空间存放数据。一般来说,系统会划分出两种不同的内存空间:一种叫做stack(栈),另一种叫做heap(堆)。
它们的主要区别是:stack是有结构的,每个区块按照一定次序存放,可以明确知道每个区块的大小;heap是没有结构的,数据可以任意存放。因此,stack的寻址速度要快于heap。
其他的区别还有,一般来说,每个线程分配一个stack,每个进程分配一个heap,也就是说,stack是线程独占的,heap是线程共用的。此外,stack创建的时候,大小是确定的,数据超过这个大小,就发生stack overflow错误,而heap的大小是不确定的,需要的话可以不断增加。
根据上面这些区别,数据存放的规则是:只要是局部的、占用空间确定的数据,一般都存放在stack里面,否则就放在heap里面。请看下面这段代码(来源)。
public void Method1()
{
int i=4;
int y=2;
class1 cls1 = new class1();
}
上面代码的Method1方法,共包含了三个变量:i, y 和 cls1。其中,i和y的值是整数,内存占用空间是确定的,而且是局部变量,只用在Method1区块之内,不会用于区块之外。cls1也是局部变量,但是类型为指针变量,指向一个对象的实例。指针变量占用的大小是确定的,但是对象实例以目前的信息无法确知所占用的内存空间大小。
这三个变量和一个对象实例在内存中的存放方式如下。
从上图可以看到,i、y和cls1都存放在stack,因为它们占用内存空间都是确定的,而且本身也属于局部变量。但是,cls1指向的对象实例存放在heap,因为它的大小不确定。作为一条规则可以记住,所有的对象都存放在heap。
接下来的问题是,当Method1方法运行结束,会发生什么事?
回答是整个stack被清空,i、y和cls1这三个变量消失,因为它们是局部变量,区块一旦运行结束,就没必要再存在了。而heap之中的那个对象实例继续存在,直到系统的垃圾清理机制(garbage collector)将这块内存回收。因此,一般来说,内存泄漏都发生在heap,即某些内存空间不再被使用了,却因为种种原因,没有被系统回收。
- CocoaTouch包含了一个称为UIEdgeInsets的结构体:( from:http://blog.bignerdranch.com/1136-rectangles-part-2/)
typedef struct UIEdgeInsets {
CGFloat top, left, bottom, right;
} UIEdgeInsets;
UIEdgeInsets表示内嵌距离结构体。正数值表示将某一矩形边向内推进,负数值表示将某一矩形边向外拉动。
CGRect UIEdgeInsetsInsetRect(CGRect rect, UIEdgeInsets insets)
UIEdgeInsetsInsetRect函数的形参包括一个被操作矩形rect以及一个UIEdgeInsets类型的insets。
如下图:将原始矩形左侧边向外拖动20点,下侧边向外拖动30点。则UIEdgeInsets值为(0.0, -20.0, -30.0, 0.0)
完整代码:
CGRect before = CGRectMake (100.0, 100.0, 200.0, 100.0);
UIEdgeInsets insets = UIEdgeInsetsMake (0.0, -20.0, -30, 0.0);
CGRect rectPlusAnnotations = UIEdgeInsetsInsetRect (before, insets);
// 输出:rectPlusAnnotations = {{80, 100}, {220, 130}}
NSLog(@"rectPlusAnnotations = %@", NSStringFromCGRect(rectPlusAnnotations));
- 矩形切分函数CGRectDivide
void CGRectDivide (CGRect inRect, CGRect *outSlice, CGRect *outRemainder,CGFloat amount, CGRectEdge edge);
功能描述:假设你有一块矩形奶油蛋糕,你现在需要切下一块分给到你家做客的小伙伴。inRect表示将被切分的蛋糕矩形。outSlice表示被切下的蛋糕矩形。outRemainder表示剩下的蛋糕矩形,amount表示下刀的距离,edge表示下刀的方位,它有四个取值:CGRectMinXEdge, CGRectMinYEdge, CGRectMaxXEdge, CGRectMaxYEdge。分别表示奶油的left,top,right,bottom边缘。 被切下的两块蛋糕矩形outSlice和outRemainder会通过矩形指针返回。
CGRect startingRect = CGRectMake (37.0, 42.0, 300.0, 100.0);
CGRect slice, remainder;
CGRectDivide (startingRect, &slice, &remainder, 100.0, CGRectMinXEdge);
桔色区域表示slice区域,棕色区域表示remainder区域。
slice值为{{37, 42} {100, 100}}
remainder值为{{137, 42}, {200, 100}}
slice不会大于原始矩形。如果将amount的100改成-100。slice将得到一个{{37, 42}, {0, 100}}的矩形,注意到矩形的宽变成了0。
CGRectDivide用法示例:
假设你现在需要在窗口内手动管理对象的布局。首先划分顶部矩形作为头区域。使用剩下的矩形作为内容区域。接着划分内容区域的底部矩形作为页脚信息区域。最后划分剩下的内容区域的两侧作为信息面板。剩下的区域即为内容区域。操作快照如下图:
完整代码:
CGRect startingRect = CGRectMake (50.00, 50.0, 100.0, 100.0);
CGRect remainder;
CGRect headerRect;
CGRectDivide (startingRect, &headerRect, &remainder, 20.0, CGRectMinYEdge);
CGRect footerRect;
CGRectDivide (remainder, &footerRect, &remainder, 10.0, CGRectMaxYEdge);
CGRect leftPanelRect;
CGRectDivide (remainder, &leftPanelRect, &remainder, 15.0, CGRectMinXEdge);
CGRect rightPanelRect;
CGRectDivide (remainder, &rightPanelRect, &remainder, 30, CGRectMaxXEdge);
CGRect contentArea = remainder;
- 如上矩形可表示为:
CGRect selection = {{ 0.0, 0.0 }, { 200.0, 100.0 }};
CGRect thing1 = {{ 10.0, 20.0 }, { 80.0, 80.0 }};
CGRect thing2 = {{ 180.0, 20.0 }, { 100.0, 40.0 }};
检查指定的区域是否被选中:
if (CGRectContainsRect(selection, thing1)) NSLog (@"thing1 selected"); if (CGRectContainsRect(selection, thing2)) NSLog (@"thing2 selected");
将打印“thing1 selected”
检查是否有交集:
if (CGRectIntersectsRect(selection, thing1)) NSLog (@"thing1 selected");
if (CGRectIntersectsRect(selection, thing2)) NSLog (@"thing2 selected");
将打印“thing1 selected”和“thing2 selected”
CGRectIntersectsRect不仅用于检查是否被选中。你可能使用该函数以检查某些图形对象是否需要绘制。如果你现在需要管理一堆需要绘制于视图的对象,调用CGRectIntersectsRect函数可以快速判断当前对象是否与视图的可视区域存在交集,以决定是否需要绘制该对象。
- 矩形正规化处理:
CGRect weirdRect = { 100.0, 100.0, -30.0, -50.0 };
CGRect standardRect = CGRectStandardize (weirdRect);
// 输出:standardizing {{100, 100}, {-30, -50}} -> {{70, 50}, {30, 50}}
NSLog (@"standardizing %@ ->%@", NSStringFromRect(weirdRect), NSStringFromRect(standardRect));
- 消除文本模糊:
CGRect differential = CGRectMake (0.5, 0.2, 99.9, 105.5);
CGRect integral = CGRectIntegral (differential); // 输出(0, 0, 101, 106)
- UIScrollView对象的属性decelerationRate包含两个属性值常量:UIScrollViewDecelerationRateFast和UIScrollViewDecelerationRateNormal。前者表示当手指离开滑动的滚动视图,滚动视图会快速停止滚动;后者则表示会让滚动视图继续滚动一段距离。对于较大的滚动视图,首先快速定位到目标点,接着缓慢的浏览(单精度比较,注意无穷小量):
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale {
scrollView.decelerationRate = scale <= scrollView.minimumZoomScale + FLT_EPSILON ? UIScrollViewDecelerationRateNormal : UIScrollViewDecelerationRateFast;
}
UIImage *originalImage = [UIImage imageNamed:@"Image.png"];
// scaling set to 2.0 makes the image 1/2 the size.
UIImage *scaledImage =[UIImage imageWithCGImage:[originalImage CGImage]
scale:(originalImage.scale * 2.0)
orientation:(originalImage.imageOrientation)];
- 创建文件夹:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents folder
NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:@"/MyFolder"];
if (![[NSFileManager defaultManager] fileExistsAtPath:dataPath]) {
[[NSFileManager defaultManager] createDirectoryAtPath:dataPath withIntermediateDirectories:NO attributes:nil error:&error]; //Create folder
}
- UIImage转NSData:
NSData *data = UIImagePNGRepresentation(image);
@interface UIImage (CacheExtensions)
+ (id)cachedImageWithContentsOfFile:(NSString *)path;
+ (void)clearCache;
@end
static NSMutableDictionary *UIImageCache;
@implementation UIImage (CacheExtensions)
+ (id)cachedImageWithContentsOfFile:(NSString *)path
{
id result;
if (!UIImageCache)
UIImageCache = [[NSMutableDictionary alloc] init];
else {
result = [UIImageCache objectForKey:path];
if (result)
return result;
}
result = [UIImage imageWithContentsOfFile:path];
[UIImageCache setObject:result forKey:path];
return result;
}
+ (void)clearCache
{
[UIImageCache removeAllObjects];
}
@end
- 代码执行时间:
NSDate *start = [NSDate date];
// do something
NSDate *methodFinish = [NSDate date];
NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:start];
NSLog(@"Execution Time: %f", executionTime);
- 将图片保存在document目录内:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"Image.png"];
[UIImagePNGRepresentation(_gridImageType) writeToFile:filePath atomically:YES];
- 图片加载:
NSString *path = [[NSBundle mainBundle] pathForResource:@"icon_new" ofType:@"png"];
- 图片文件大小:
UIImage *originalImage = (UIImage*)[info valueForKey:UIImagePickerControllerOriginalImage];
NSData *imgData = UIImageJPEGRepresentation(originalImage, 1.0);
NSLog(@"Size of Image(bytes):%d",[imgData length]);
- 读取本地xml文件生成NSString:
NSError *errorReading;
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"recordDetail3" ofType:@"xml"];
NSString* xmlString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&errorReading];
- 日期转字符并替换:
NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSDate *now = [NSDate date];
NSString *dateFromString = [formatter stringFromDate:now];
NSMutableString * mutableString = [@"time" mutableCopy];
[mutableString replaceCharactersInRange:NSMakeRange(0, inspectorTime.length) withString:dateFromString];
- XML非法字符过滤:主要了解分类中使用属性以及正则表达式的使用。
- CGSize和CGRect的比较:CGSizeEqualToSize和CGRectEqualToRect函数。
- UITableView中的手势:
UIPinchGestureRecognizer *pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)];
[self.tableView addGestureRecognizer:pinchRecognizer];
- (void)handlePinch:(UIPinchGestureRecognizer *)pinchRecognizer {
if (pinchRecognizer.state == UIGestureRecognizerStateBegan) {
CGPoint pinchLocation = [pinchRecognizer locationInView:self.tableView];
NSIndexPath *newPinchedIndexPath = [self.tableView indexPathForRowAtPoint:pinchLocation];
self.pinchedIndexPath = newPinchedIndexPath;
}
- 往UITableview插入一个显示在UITableViewCell后方的子视图,使用UITableView的backgroundView属性实现。使用场景:点击某一个cell后高亮视图从原始位置滑到目标Cell。from:http://stackoverflow.com/questions/9265058/ios-add-uiview-to-uitableview
- 获取调用当前所在类中公/私有方法的源类名: from:http://stackoverflow.com/questions/1451342/objective-c-find-caller-of-method
NSString *sourceString = [[NSThread callStackSymbols] objectAtIndex:1];
NSCharacterSet *separatorSet = [NSCharacterSet characterSetWithCharactersInString:@" -[]+?.,"];
NSMutableArray *array = [NSMutableArray arrayWithArray:[sourceString componentsSeparatedByCharactersInSet:separatorSet]];
[array removeObject:@""];
NSLog(@"Stack = %@", [array objectAtIndex:0]);
NSLog(@"Framework = %@", [array objectAtIndex:1]);
NSLog(@"Memory address = %@", [array objectAtIndex:2]);
NSLog(@"Class caller = %@", [array objectAtIndex:3]);
NSLog(@"Function caller = %@", [array objectAtIndex:4]);
NSLog(@"Line caller = %@", [array objectAtIndex:5]);}
- 获取一个类所有创建过的对象:from: http://stackoverflow.com/questions/2925405/objective-c-class-method-to-control-all-existing-instances
static NSMutableArray *allObjects = nil;
+ (void) initialize {
if (!allObjects) {
allObjects = [NSMutableArray new];
}
}
- (id) init {
if (self = [super init]) {
//Each object gets held in an array
[allObjects addObject:self];
}
return self;
}
+ (void) doSomethingToEveryObject {
[allObjects makeObjectsPerformSelector:@selector(doSomethingToThisObject)];
}
- (void) doSomethingToThisObject {
NSLog(@"Did something to %@",self);
}
- (void)dealloc {
[allObjects removeObject:self];
}
AFNetworking 2.0发送JSON数据:
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSString *URLString = @"http://183.129.228.70:8085/api/FeedBack/PostFeedBack";
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:URLString]];
urlRequest.HTTPMethod = @"POST";
urlRequest.HTTPBody = [xmlDocument dataUsingEncoding:NSUTF8StringEncoding];
AFHTTPRequestOperation *operation = [manager HTTPRequestOperationWithRequest:urlRequest success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"%@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
[operation start];
- 代码选中某一行
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
[tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
ceil(x)返回不小于x的最小整数值(然后转换为double型)。
floor(x)返回不大于x的最大整数值。
round(x)返回x的四舍五入整数值。
- 使用代码创建一个和IB编辑器一样外观的UITextField:
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(10, 200, 300, 40)];
textField.borderStyle = UITextBorderStyleRoundedRect;
textField.font = [UIFont systemFontOfSize:15];
textField.placeholder = @"enter text";
textField.autocorrectionType = UITextAutocorrectionTypeNo;
textField.keyboardType = UIKeyboardTypeDefault;
textField.returnKeyType = UIReturnKeyDone;
textField.clearButtonMode = UITextFieldViewModeWhileEditing;
textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
textField.delegate = self;
[self.view addSubview:textField];
- 添加下落阴影:from: http://stackoverflow.com/questions/9761189/whats-the-best-way-to-add-a-drop-shadow-to-my-uiview
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:view.bounds];
view.layer.masksToBounds = NO;
view.layer.shadowColor = [UIColor blackColor].CGColor;
view.layer.shadowOffset = CGSizeMake(0.0f, 5.0f);
view.layer.shadowOpacity = 0.5f;
view.layer.shadowPath = shadowPath.CGPath;
- UITextFiled Left Padding: from:http://stackoverflow.com/questions/5674655/uitextfield-align-left-margin
myTextField.layer.sublayerTransform = CATransform3DMakeTranslation(5, 0, 0);
- 羽化效果,给用户下面还有更多内容的暗示:from: http://www.cocoanetics.com/2011/08/adding-fading-gradients-to-uitableview/
完整代码:
CAGradientLayer *moreLayer = [CAGradientLayer layer];
CGColorRef outerColor = [UIColor colorWithWhite:1.0 alpha:0.0].CGColor;
CGColorRef middleColor1 = [UIColor colorWithWhite:1.0 alpha:0.1].CGColor;
CGColorRef middleColor2 = [UIColor colorWithWhite:1.0 alpha:0.9].CGColor;
CGColorRef innerColor = [UIColor colorWithWhite:1.0 alpha:1.0].CGColor;
moreLayer.colors = @[(__bridge id)innerColor, (__bridge id)middleColor1, (__bridge id)outerColor, (__bridge id)outerColor, (__bridge id)middleColor2, (__bridge id)innerColor];
moreLayer.locations = @[@0.01, @0.034, @0.035, @0.90, @.95, @1.0];
moreLayer.bounds = CGRectMake(0, 0, CGRectGetWidth(_detailView.frame), CGRectGetHeight(_detailView.frame));
moreLayer.anchorPoint = CGPointZero;
moreLayer.position = CGPointMake(CGRectGetWidth(_inspctorTableView.frame), 0);
[self.layer addSublayer:moreLayer];