05-2417:17:47.3352444724478 D EventBusActivity: 2447805-2417:17:48.3412444724478 D EventBusActivity: this is first message ! onEventBackground thread:2447805-2417:17:48.3412444724498 D EventBusActivity: this is first message ! onEventAsync thread:2449805-2417:17:48.3422444724447 D EventBusActivity: this is first message ! onEventMain thread:2444705-2417:17:48.3422444724447 D EventBusActivity: this is first message ! onEventMainOrdered thread:2444705-2417:17:48.3442444724478 D EventBusActivity: this is first message ! onEventPosting thread:24478
print("The width of someResolution is \(someResolution.width)")
// 打印 "The width of someResolution is 0"print("The width of someVideoMode is \(someVideoMode.resolution.width)")
// 打印 "The width of someVideoMode is 0"
someVideoMode.resolution.width = 1280print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
// 打印 "The width of someVideoMode is now 1280"
enumCompassPoint{
caseNorth, South, East, West
}
var currentDirection = CompassPoint.Westlet rememberedDirection = currentDirection
currentDirection = .Eastif rememberedDirection == .West {
print("The remembered direction is still .West")
}
// 打印 "The remembered direction is still .West"
let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// 打印 "The frameRate property of theEighty is now 30.0"
以上示例中,声明了一个名为tenEighty的常量,其引用了一个VideoMode了的新实例。在之前的示例中,这个视频模式被赋予了HD分辨率(1920*1080)的一个拷贝(即 hd 实例)。同时设置为interlaced,命名为“1080i”。最为帧率为25.0帧每秒。然后,tenEighty 被赋予名为 alsoTenEighty 的新常量,同时对 alsoTenEighty 的帧率进行修改。
if tenEighty === alsoTenEighty {
print("tenEighty and alsoTenEighty refer to the same Resolution instance.")
}
//打印 "tenEighty and alsoTenEighty refer to the same Resolution instance."
在 A 的初始化方法中,我们生成了一个 B 的实例并将其存储在属性中。然后我们又将 A 的实例赋值给了 b.a。这样 a.b 和 b.a 将在初始化的时候形成一个引用循环。现在当有第三方的调用初始化了 A,然后即使立即将其释放,A 和 B 两个类实例的 deinit 方法也不会被调用,说明它们并没有被释放。
varobj:A?=A()obj=nil// 内存没有释放
因为即使 obj 不再持有 A 的这个对象,b 中的 b.a 依然引用着这个对象,导致它无法释放。而进一步,a 中也持有着 b,导致 b 也无法释放。在将 obj 设为 nil 之后,我们在代码里再也拿不到对于这个对象的引用了,所以除非是杀掉整个进程,我们已经永远也无法将它释放了。多么悲伤的故事啊..
在 Swift 里防止循环引用
为了防止这种人神共愤的悲剧的发生,我们必须给编译器一点提示,表明我们不希望它们互相持有。一般来说我们习惯希望 “被动” 的一方不要去持有 “主动” 的一方。在这里 b.a 里对 A 的实例的持有是由 A 的方法设定的,我们在之后直接使用的也是 A 的实例,因此认为 b 是被动的一方。可以将上面的 class B 的声明改为:
classB{weakvara:A?=nildeinit{print("B deinit")}}
在 var a 前面加上了 weak,向编译器说明我们不希望持有 a。这时,当 obj 指向 nil 时,整个环境中就没有对 A 的这个实例的持有了,于是这个实例可以得到释放。接着,这个被释放的实例上对 b 的引用 a.b 也随着这次释放结束了作用域,所以 b 的引用也将归零,得到释放。添加 weak 后的输出:
classPerson{letname:StringlazyvarprintName:()->()={print("The name is \(self.name)")}init(personName:String){name=personName}deinit{print("Person deinit \(self.name)")}}varxiaoMing:Person?=Person(personName:"XiaoMing")xiaoMing!.printName()xiaoMing=nil// 输出:// The name is XiaoMing,没有被释放
Categories are an Objective-C language feature that let you add new methods to an existing class, much like C# extensions. However, do not confuse C# extensions with Objective-C extensions. Objective-C’s extensions are a special case of categories that let you define methods that must be declared in the main implementation block.
These are powerful features that have many potential uses. First, categories make it possible to split up a class’ interface and implementation into several files, which provides much-needed modularity for larger projects. Second, categories let you fix bugs in an existing class (e.g., NSString) without the need to subclass it. Third, they provide an effective alternative to the protected and private methods found in C# and other Simula-like languages.
Categories
A category is a group of related methods for a class, and all of the methods defined in a category are available through the class as if they were defined in the main interface file. As an example, take the Person class that we’ve been working with throughout this book. If this were a large project, Person may have dozens of methods ranging from basic behaviors to interactions with other people to identity checking. The API might call for all of these methods to be available through a single class, but it’s much easier for developers to maintain if each group is stored in a separate file. In addition, categories eliminate the need to recompile the entire class every time you change a single method, which can be a time-saver for very large projects.
Let’s take a look at how categories can be used to achieve this. We start with a normal class interface and a corresponding implementation:
// Person.h
@interfacePerson: NSObject
@interfacePerson: NSObject
@property(readonly) NSMutableArray* friends;
@property(copy) NSString* name;
- (void)sayHello;
- (void)sayGoodbye;
@end
// Person.m
#import "Person.h"
@implementationPerson
@synthesizename = _name;
@synthesizefriends = _friends;
-(id)init{
self= [superinit];
if(self){
_friends = [[NSMutableArrayalloc]init];
}
returnself;
}
- (void)sayHello {
NSLog(@"Hello, says %@.", _name);
}
- (void)sayGoodbye {
NSLog(@"Goodbye, says %@.", _name);
}
@end
Nothing new here-just a Person class with two properties (the friends property will be used by our category) and two methods. Next, we’ll use a category to store some methods for interacting with other Person instances. Create a new file, but instead of a class, use the Objective-C Category template. Use Relations for the category name and Person for the Category on field:
Creating the Person+Relations class
As expected, this will create two files: a header to hold the interface and an implementation. However, these will both look slightly different than what we’ve been working with. First, let’s take a look at the interface:
// Person+Relations.h
#import <Foundation/Foundation.h>
#import "Person.h"
@interfacePerson(Relations)
- (void)addFriend:(Person*)aFriend;
- (void)removeFriend:(Person*)aFriend;
- (void)sayHelloToFriends;
@end
Instead of the normal @interface declaration, we include the category name in parentheses after the class name we’re extending. A category name can be anything, as long as it doesn’t conflict with other categories for the same class. A category’s file name should be the class name followed by a plus sign, followed by the name of the category (e.g., Person+Relations.h ).
So, this defines our category’s interface. Any methods we add in here will be added to the original Person class at run time. It will appear as though the addFriend:, removeFriend:, and sayHelloToFriends methods are all defined in Person.h, but we can keep our functionality encapsulated and maintainable. Also note that you must import the header for the original class, Person.h. The category implementation follows a similar pattern:
// Person+Relations.m
#import "Person+Relations.h"
@implementationPerson(Relations)
- (void)addFriend:(Person*)aFriend {
[[selffriends]addObject:aFriend];
}
- (void)removeFriend:(Person*)aFriend {
[[selffriends]removeObject:aFriend];
}
- (void)sayHelloToFriends {
for(Person*friend in[selffriends]) {
NSLog(@"Hello there, %@!", [friendname]);
}
}
@end
This implements all of the methods in Person+Relations.h. Just like the category’s interface, the category name appears in parentheses after the class name. The category name in the implementation should match the one in the interface.
Also, note that there is no way to define additional properties or instance variables in a category. Categories have to refer back to data stored in the main class (friends in this instance).
It’s also possible to override the implementation contained in Person.m by simply redefining the method in Person+Relations.m. This can be used to monkey patch an existing class; however, it’s not recommended if you have an alternative solution to the problem, since there would be no way to override the implementation defined by the category. That is to say, unlike the class hierarchy, categories are a flat organizational structure-if you implement the same method in two separate categories, it’s impossible for the runtime to figure out which one to use.
The only change you have to make to use a category is to import the category’s header file. As you can see in the following example, the Person class has access to the methods defined in Person.h along with those defined in the category Person+Relations.h:
// main.m
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Person+Relations.h"
intmain(intargc, constchar* argv[]) {
@autoreleasepool{
Person*joe = [[Personalloc]init];
joe.name= @"Joe";
Person*bill = [[Personalloc]init];
bill.name= @"Bill";
Person*mary = [[Personalloc]init];
mary.name= @"Mary";
[joesayHello];
[joeaddFriend:bill];
[joeaddFriend:mary];
[joesayHelloToFriends];
}
return0;
}
And that’s all there is to creating categories in Objective-C.
Protected Methods
To reiterate, all Objective-C methods are public-there is no language construct to mark them as either private or protected. Instead of using “true” protected methods, Objective-C programs can combine categories with the interface/implementation paradigm to achieve the same result.
The idea is simple: declare “protected” methods as a category in a separate header file. This gives subclasses the ability to “opt-in” to the protected methods while unrelated classes use the “public” header file as usual. For example, take a standard Ship interface:
// Ship.h
#import <Foundation/Foundation.h>
@interfaceShip: NSObject
- (void)shoot;
@end
As we’ve seen many times, this defines a public method called shoot. To declare a protected method, we need to create a Ship category in a dedicated header file:
// Ship_Protected.h
#import <Foundation/Foundation.h>
@interfaceShip(Protected)
- (void)prepareToShoot;
@end
Any classes that need access to the protected methods (namely, the superclass and any subclasses) can simply import Ship_Protected.h. For example, the Ship implementation should define a default behavior for the protected method:
// Ship.m
#import "Ship.h"
#import "Ship_Protected.h"
@implementationShip{
BOOL_gunIsReady;
}
- (void)shoot {
if(!_gunIsReady) {
[selfprepareToShoot];
_gunIsReady = YES;
}
NSLog(@"Firing!");
}
- (void)prepareToShoot {
// Execute some private functionality.
NSLog(@"Preparing the main weapon...");
}
@end
Note that if we hadn’t imported Ship_Protected.h, this prepareToShoot implementation would be a private method, as discussed in the Methods chapter. Without a protected category, there would be no way for subclasses to access this method. Let’s subclass the Ship to see how this works. We’ll call it ResearchShip:
// ResearchShip.h
#import "Ship.h"
@interfaceResearchShip: Ship
- (void)extendTelescope;
@end
This is a normal subclass interface-it should not import the protected header, as this would make the protected methods available to anyone that imports ResearchShip.h, which is precisely what we’re trying to avoid. Finally, the implementation for the subclass imports the protected methods and (optionally) overrides them:
// ResearchShip.m
#import "ResearchShip.h"
#import "Ship_Protected.h"
@implementationResearchShip
- (void)extendTelescope {
NSLog(@"Extending the telescope");
}
// Override protected method
- (void)prepareToShoot {
NSLog(@"Oh shoot! We need to find some weapons!");
}
@end
To enforce the protected status of the methods in Ship_Protected.h, other classes aren’t allowed to import it. They’ll just import the normal “public” interfaces of the superclass and subclass:
// main.m
#import <Foundation/Foundation.h>
#import "Ship.h"
#import "ResearchShip.h"
intmain(intargc, constchar* argv[]) {
@autoreleasepool{
Ship*genericShip = [[Shipalloc]init];
[genericShipshoot];
Ship*discoveryOne = [[ResearchShipalloc]init];
[discoveryOneshoot];
}
return0;
}
Since neither main.m, Ship.h, nor ResearchShip.h import the protected methods, this code won’t have access to them. Try adding a [discoveryOne prepareToShoot] method-it will throw a compiler error, since the prepareToShoot declaration is nowhere to be found.
To summarize, protected methods can be emulated by placing them in a dedicated header file and importing that header file into the implementation files that require access to the protected methods. No other files should import the protected header.
While the workflow presented here is a completely valid organizational tool, keep in mind that Objective-C was never meant to support protected methods. Think of this as an alternative way to structure an Objective-C method, rather than a direct replacement for C#/Simula-style protected methods. It’s often better to look for another way to structure your classes rather than forcing your Objective-C code to act like a C# program.
Caveats
One of the biggest issues with categories is that you can’t reliably override methods defined in categories for the same class. For example, if you defined an addFriend: class in Person(Relations) and later decided to change the addFriend: implementation via a Person(Security) category, there is no way for the runtime to know which method it should use since categories are, by definition, a flat organizational structure. For these kinds of cases, you need to revert to the traditional subclassing paradigm.
Also, it’s important to note that a category can’t add instance variables. This means you can’t declare new properties in a category, as they could only be synthesized in the main implementation. Additionally, while a category technically does have access to its classes’ instance variables, it’s better practice to access them through their public interface to shield the category from potential changes in the main implementation file.
Extensions
Extensions (also called class extensions) are a special type of category that requires their methods to be defined in the main implementation block for the associated class, as opposed to an implementation defined in a category. This can be used to override publicly declared property attributes. For example, it is sometimes convenient to change a read-only property to a read-write property within a class’ implementation. Consider the normal interface for a Ship class:
Included code sample: Extensions
// Ship.h
#import <Foundation/Foundation.h>
#import "Person.h"
@interfaceShip: NSObject
@property(strong, readonly) Person*captain;
- (id)initWithCaptain:(Person*)captain;
@end
It’s possible to override the @property definition inside of a class extension. This gives you the opportunity to re-declare the property as readwrite in the implementation file. Syntactically, an extension looks like an empty category declaration:
// Ship.m
#import "Ship.h"
// The class extension.
@interfaceShip()
@property(strong, readwrite) Person*captain;
@end
// The standard implementation.
@implementationShip
@synthesizecaptain = _captain;
- (id)initWithCaptain:(Person*)captain {
self= [superinit];
if(self) {
// This WILL work because of the extension.
[selfsetCaptain:captain];
}
returnself;
}
@end
Note the () appended to the class name after the @interface directive. This is what marks it as an extension rather than a normal interface or a category. Any properties or methods that appear in the extension must be declared in the main implementation block for the class. In this case, we aren’t adding any new fields-we’re overriding an existing one. But unlike categories, extensions can add extra instance variables to a class, which is why we’re able to declare properties in a class extension but not a category.
Because we re-declared the captain property with a readwrite attribute, the initWithCaptain: method can use the setCaptain: accessor on itself. If you were to delete the extension, the property would return to its read-only status and the compiler would complain. Clients using the Ship class aren’t supposed to import the implementation file, so the captain property will remain read-only.
// This will NOT work because the property is still read-only.
[discoveryOnesetCaptain:dave];
}
return0;
}
Private Methods
Another common use case for extensions is for declaring private methods. In the previous chapter, we saw how private methods can be declared by simply adding them anywhere in the implementation file. But, prior to Xcode 4.3, this was not the case. The canonical way to create a private method was to forward-declare it using a class extension. Let’s take a look at this by slightly altering the Ship header from the previous example:
// Ship.h
#import <Foundation/Foundation.h>
@interfaceShip: NSObject
- (void)shoot;
@end
Next, we’re going to recreate the example we used when we discussed private methods in the Methods chapter. Instead of simply adding the private prepareToShoot method to the implementation, we need to forward-declare it in a class extension.
// Ship.m
#import "Ship.h"
// The class extension.
@interfaceShip()
- (void)prepareToShoot;
@end
// The rest of the implementation.
@implementationShip{
BOOL_gunIsReady;
}
- (void)shoot {
if(!_gunIsReady) {
[selfprepareToShoot];
_gunIsReady = YES;
}
NSLog(@"Firing!");
}
- (void)prepareToShoot {
// Execute some private functionality.
NSLog(@"Preparing the main weapon...");
}
@end
The compiler ensures the extension methods are implemented in the main implementation block, which is why it functions as a forward-declaration. Yet because the extension is encapsulated in the implementation file, other objects shouldn’t ever know about it, giving us another way to emulate private methods. While newer compilers save you this trouble, it’s still important to understand how class extensions work, as it has been a common way to leverage private methods in Objective-C programs until very recently.
Summary
This chapter covered two of the more unique concepts in the Objective-C programming language: categories and extensions. Categories are a way to extend the API of existing classes, and extensions are a way to add required methods to the API outside of the main interface file. Both of these were initially designed to ease the burden of maintaining large code bases.
The next chapter continues our journey through Objective-C’s organizational structures. We’ll learn how to define a protocol, which is an interface that can be implemented by a variety of classes.
from mako.template import Template
mytemplate = Template("hello world!")
print mytemplate.render()
-------------------------
from mako.template import Template
print Template("hello ${data}!").render(data="world")
语法
输出变量 ${x}
数学计算 ${1+1}
the contents within the ${} tag are evaluated by Python directly, so full expressions are OK
filter
${"test"|u}
${"test"|u,trim}
内置filter列表
u : URL escaping, provided by urllib.quote_plus(string.encode('utf-8'))
h : HTML escaping, provided by markupsafe.escape(string)
x : XML escaping
trim : whitespace trimming, provided by string.strip()
entity : produces HTML entity references for applicable strings, derived from htmlentitydefs
unicode (str on Python 3): produces a Python unicode string (this function is applied by default)
decode.<some encoding> : decode input into a Python unicode with the specified encoding
n : disable all default filtering; only filters specified in the local expression tag will be applied.
分支
% if x == 5:
abcd
% endif
循环
% for a in ['1', '2', '3']:
% if a == '1':
abc
% elif a == '2':
def
% else:
gh
% endif
$ endfor
Python语法
this is a template
<%
x = db.get_resource('foo')
y = [z.element for z in x if x.frobnizzle==5]
%>
% for elem in y:
element: ${elem}
% endfor
换行
加 / 强制不换行
设置变量
% for item in ('apple', 'banana'):
<%
isBanana = False
%>
% if item == 'banana':
<%
isBanana = True
%>
%endif
% if isBanana:
<span> Bought a banana</span>
%endif
%endfor
Zookeeper 能够很容易的实现集群管理的功能,如有多台 Server 组成一个服务集群,那么必须要一个master知道当前集群中每台机器的服务状态,一旦有机器不能提供服务,集群中其它节点必须知道,从而做出调整重新分配服务策略。同样当增加集群的服务能力时,就会增加一台或多台 Server,同样也必须让master知道。Zookeeper 不仅能够帮你维护当前的集群中机器的服务状态,而且能够帮你选出一个master,让这个master来管理集群,这就是 Zookeeper 的另一个功能 Leader Election。
它们的实现方式都是在 Zookeeper 上创建一个 EPHEMERAL 类型的目录节点,然后每个 Server 在它们创建目录节点的父目录节点上调用getChildren(String path, boolean watch) 方法并设置 watch 为 true,由于是 EPHEMERAL 目录节点,当创建它的 Server 死去,这个目录节点也随之被删除,所以 Children 将会变化,这时 getChildren上的 Watch 将会被调用,所以其它 Server 就知道已经有某台 Server 死去了。新增 Server 也是同样的原理。
Zookeeper 如何实现 Leader Election,也就是选出一个 Master Server。和前面的一样每台 Server 创建一个 EPHEMERAL 目录节点,不同的是它还是一个 SEQUENTIAL 目录节点,所以它是个 EPHEMERAL_SEQUENTIAL 目录节点。之所以它是 EPHEMERAL_SEQUENTIAL 目录节点,是因为我们可以给每台 Server 编号,我们可以选择当前是最小编号的 Server 为 Master,假如这个最小编号的 Server 死去,由于是 EPHEMERAL 节点,死去的 Server 对应的节点也被删除,所以当前的节点列表中又出现一个最小编号的节点,我们就选择这个节点为当前 Master。这样就实现了动态选择 Master,避免了传统意义上单 Master 容易出现单点故障的问题。