swift closure capture list

最近用swift写app,发现很多特性都很有意思,并且很实用,比如closure, Optional类型等等。closure可以看做是objc中的block,c++和python中的lambda,熟悉函数式编程的同学可以很快上手。

在进行异步的网络调用时,需要把closure作为参数传递给执行网络请求的函数。比如NSURLSessionDataTask,需要把网络请求执行完毕之后的逻辑写在completionHandler里面。

很多时候我们需要在UIViewContronller里面来执行网络请求,请求完成后再更新ui上的数据,那么这里的closure中肯定会访问到self.someProperty或者self.someMethod()。这样closure本身就有一个对self的强引用(strong referrence)。另外,closure在swift中跟class一样,也是引用类型(referrence type)。这样一来closure和self就互相引用,产生strong referrence cycle。closure和self都无法被deallocate,造成资源的浪费。

为了解决这个问题,就要用到closure的捕获列表。语法如下:

捕获列表中可以使用两种类型:unowned和weak。因为一开始没有弄清楚这两种的区别,直接在代码中使用了unowned self,造成app在运行过程中出现“attempted to retain deallocated object”错误。

这段代码中第29行可以看到,在closure中访问self时并没有判断self是否还存在。所以当网络请求执行完再更新ui的时候就会出现“attempted to retain deallocated object”。unowned和weak的一个区别在于weak self是optional类型。这样一来就可以利用optional chaining来做一个保护机制。正确的实现代码如下:

这里的29行使用了swift中的if let结构,如果photoView是有值的,则继续执行后面的逻辑,否则会走到else分支中。这样便很好的解决了上面的问题。

参考:

  1. StackOverflow – Shall we always use [unowned self] inside closure in Swift?
  2. Automatic Reference Counting
  3. Swift Optionals: When to use if let, when ? and !, when as? and as

objective-c protected instance variable

最近在做斯坦福公开课cs193p的课后练习Machismo,其中用到了类的继承。

[ccb_objc]
// GameViewController.h
@interface GameViewController : UIViewController
@property (nonatomic) Game *game;
@property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *cardButtons;
@property (weak, nonatomic) IBOutlet UILabel *scoreLabel;
@property (weak, nonatomic) IBOutlet UILabel *messageLabel;
@end

// CardGameViewController.h
@interface CardGameViewController : GameViewController
@property (nonatomic) CardMatchingGame *game;
@end
[/ccb_objc]

[ccib_objc]CardGameViewController[/ccib_objc]是[ccib_objc]GameViewController[/ccib_objc]的一个子类。因为在子类中需要初始化[ccib_objc]@property (nonatomic) CardMatchingGame *game[/ccib_objc],所以就要在子类中访问基类中的instance variable,也就是[ccib_objc]_game[/ccib_objc]。如果这样尝试的话,编译器会给出一个错误。因为[ccib_objc]_game[/ccib_objc]是基类的private instance variable,子类是无法访问的。那如何才能将这个instance variable变为protected?只要将[ccib_objc]_game[/ccib_objc]在interface中声明即可。
[ccb_objc]
// GameViewController.h
@interface GameViewController : UIViewController {
Game *_game;
}
@property (nonatomic) Game *game;
@property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *cardButtons;
@property (weak, nonatomic) IBOutlet UILabel *scoreLabel;
@property (weak, nonatomic) IBOutlet UILabel *messageLabel;
@end
[/ccb_objc]
在interface中声明的变量,默认都是[ccib_objc]@protected[/ccib_objc]。如果想定义为其他类型,可以使用[ccib_objc]@public[/ccib_objc]和[ccib_objc]@private[/ccib_objc]等关键字。这点和C++还是类似的。更多详细内容请参阅下面的资料。

参考

  1. How are declared private ivars different from synthesized ivars?
  2. Are synthesized instance variables generated as private instead of protected?
  3. Objective-C Access To Instance Variables
  4. Private properties, methods and ivars in Objective-C
  5. Objective-C Tuesdays: instance variables