StackOverflow 黑魔法系列 <1>

最近看到了很多关于 iOS 开发的黑魔法, 发现最终的链接都是来自与 stackoverflow, 我感觉需要在 stackoverflow 上这个社区逛一逛, 找一些比较屌~~神奇的黑魔法来分享一下, 也能够多两篇水文~~提高一下自己的水平, 当然我会在每一条”咒语”的后面加上原文的链接.

原子(atomic)和非原子(nonatomic)的区别是什么?

@property(nonatomic, retain) UITextField *userName;
@property(atomic, retain) UITextField *userName;
@property(retain) UITextField *userName;

这三行代码中的后两行是相同的, 因为 atomic 是默认的行为. 在现在的 LLVM 的版本中, 使用 atomicnonatomic 声明的属性在自动生成 getter/setter 方法时有所不同.

如果你是用了 atomic, 那么自动合成的 getter/setter 方法会确保值会正确从 getter 中返回, 或者能够正确的被 setter 所设置, 也就是加锁. 同一时间只能访问(包含 getter/setter)这个属性一次.

nonatomic 中, 就不会保证同一时间只有一个操作能够访问该属性了, 这也会加快属性的访问速度, 使得 nonatomicatomic 快的多.

然而 atomic 并不能保证线程的安全, 如果在 线程 A 中调用 getter, 与此同时在 线程 B线程 C 中调用 setter, 那么 线程 A 中可能获得 B/C 中某一个属性的值. 但是我们并不知道结果. 所以, atomic 虽然能在一定程度上确保 getter/setter 方法的正确调用但是并不能 100% 的确保线程的安全.

而确保线程安全和数据完整, 也是我们在使用多线程编程中的主要挑战, 它可以通过别的方法来解决.

链接: What’s the difference between the atomic and nonatomic attributes?


如何使用 iPhone SDK 检查当前网络的状态?

检查当前网络的状态还是有多种方法的.

提问的人首先提了一个非常取巧也非常聪明的办法, 它直接使用了 stringWithContentsOfURL: 这个方法来访问 Google, 然后查看是否有 response 来判断网络的状态.

- (BOOL) connectedToInternet {
  NSString *URLString = [NSString stringWithContentsOfURL:[NSURL URLWithString:@"http://www.google.com"]];
  return ( URLString != NULL ) ? YES : NO;
}

但是这段代码在天朝还是行不通啊, 我们并没有 Google 这个网站, 当然可以把 Google 改成随便什么别的网站来解决这个问题, 但是我们还是需要一种更加”靠谱”的解决方案.

第二种方法是使用第三方开源框架 Reachability

在实现文件的中添加如下代码, 然后填写你想要测试连通性的 host:

// Checks if we have an internet connection or not
    - (void)testInternetConnection {   
    // Allocate a reachability object
    Reachability* reach = [Reachability reachabilityWithHostname:@"www.google.com"];

    // Set the blocks
    reach.reachableBlock = ^(Reachability*reach) {
    // keep in mind this is called on a background thread
    // and if you are updating the UI it needs to happen
    // on the main thread, like this:

        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"REACHABLE!");
        });
    };

    reach.unreachableBlock = ^(Reachability*reach) {
        NSLog(@"UNREACHABLE!");
    };

    // Start the notifier, which will cause the reachability object to retain itself!
    [reach startNotifier];
}

链接: How to check for an active Internet Connection on iPhone SDK?


在 Objective-C 的字符串中如何查询是否含有子字符串?

在 iOS8 以前, 我们需要使用 rangeOfString: 方法来判断是否含有子字符串:

NSString *string = @"hello bla bla";
if ([string rangeOfString:@"bla"].location == NSNotFound) {
   NSLog(@"string does not contain bla");
} else {
   NSLog(@"string contains bla!");
}

其中最关键的地方是 rangeOfString: 会返回一个 NSRange 的结构体, {NSNotFound, 0}, 所以我们要获取结构体中的 location 来判断是否存在该字符串, 详见文档.

而在 iOS8 之后, 苹果大发慈悲地为我们提供了更加友善的解决办法, 也就是 containString: 方法直接解决了这个问题.

NSString *string = @"hello bla blah";
if ([string containsString:@"bla"]) {
   NSLog(@"string contains bla!");
} else {
   NSLog(@"string does not contain bla");
}

链接: How do I check if a string contains another string in Objective-C?

当键盘出现时, 如何使 UITextField 向上移动?

在 iOS 开发中, 我们经常会遇到这样的问题, 键盘的出现会遮挡已有的文本框使得用户无法看到自己的输入. 这其实是一个比较麻烦的问题, 并不是一两行代码能够解决的. 在这个问题中, 需要注意两点:

  • 如果你所要显示的内容并没有全部在 iPhone 屏幕中, 那么就需要添加一个 UIScrollView, 也就是说, 如果仅仅为了使得 UITextField 上移, 添加 UIScrollView 是得不偿失并且浪费的.
  • 解决这个问题最标准的办法就是, 在键盘出现的时候上移 UITextField, 在键盘消失的时候下移 UITextField.

在这里我们要使用通知来解决这个问题, 首先我们要在 viewWillAppear:viewWillDisapear: 中分别注册和移除通知.

- (void)viewWillAppear:(BOOL)animated {
   [super viewWillAppear:animated];
   [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow) name:UIKeyboardWillShowNotification object:nil];
   [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide) name:UIKeyboardWillHideNotification object:nil];
}

- (void)viewWillDisappear:(BOOL)animated {
   [super viewWillDisappear:animated];
   [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
   [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

然后我们需要实现一个 UITextField 的代理方法 textFieldDidBeginEditing:UITextField 开始编辑键盘准备响应的时候, 让 UITextField 向上弹出.

- (void)textFieldDidBeginEditing:(UITextField *)sender {
   //move the main view, so that the keyboard does not hide it.
   if  (self.view.frame.origin.y >= 0) {
       [self setViewMovedUp:YES];
   }
}

接下来实现当键盘弹出或隐藏发出通知所响应的两个方法, keyboardWillShowkeyboardWillHide.

- (void)keyboardWillShow {
   // Animate the current view out of the way
   if (self.view.frame.origin.y >= 0) {
       [self setViewMovedUp:YES];
   } else if (self.view.frame.origin.y < 0) {
       [self setViewMovedUp:NO];
   }
}

- (void)keyboardWillHide {
   if (self.view.frame.origin.y >= 0) {
       [self setViewMovedUp:YES];
   } else if (self.view.frame.origin.y < 0) {
       [self setViewMovedUp:NO];
   }
}

在最后, 就来实现使 UITextField 移动的方法 setViewMovedUp: 了.

#define kOFFSET_FOR_KEYBOARD 80.0

- (void)setViewMovedUp:(BOOL)movedUp {
   [UIView beginAnimations:nil context:NULL];
   [UIView setAnimationDuration:0.3]; // if you want to slide up the view

   CGRect rect = self.view.frame;
   if (movedUp) {
       // 1. move the view's origin up so that the text field that will be hidden come above the keyboard
       // 2. increase the size of the view so that the area behind the keyboard is covered up.
       rect.origin.y -= kOFFSET_FOR_KEYBOARD;
       rect.size.height += kOFFSET_FOR_KEYBOARD;
   } else {
       // revert back to the normal state.
       rect.origin.y += kOFFSET_FOR_KEYBOARD;
       rect.size.height -= kOFFSET_FOR_KEYBOARD;
   }
   self.view.frame = rect;

   [UIView commitAnimations];
}

这种重要的问题在苹果的官方文档中也对应的实现方法.

链接: How to make a UITextField move up when keyboard is present


如何在 UITableView 中禁止选择的高亮?

tableView.allowsSelection = NO;

链接: How can I disable the UITableView selection highlighting?

关于图片和转载

知识共享许可协议
本作品采用知识共享署名 4.0 国际许可协议进行许可。 转载时请注明原文链接,图片在使用时请保留图片中的全部内容,可适当缩放并在引用处附上图片所在的文章链接,图片使用 Sketch 进行绘制。

关于评论和留言

如果对本文 StackOverflow 黑魔法系列 <1> 的内容有疑问,请在下面的评论系统中留言,谢谢。

原文链接:StackOverflow 黑魔法系列 <1> · 面向信仰编程

Follow: Draveness · GitHub

Draveness

Rails / Elixir / iOS

Beijing, China draveness.me