Monday 17 June 2013

iOS TableView Part I Cell Reused.

1.  我们为什么要重用cell?
 答案很简单,就是performance的原因。
 每次当我们滑动的时候,遇到未知的位置,我们就需要找到个cell来填充,这时候我们可以选  择创建个cell出来,然后把需要的信息填进cell,这样的坏处是什么呢?
Object allocation 是一个非常消耗效率和时间的事,而且特别在一个很短周期内重复地alloc Object。所以这时候当我们scroll table view的时候,我们就可以利用已经被划出屏幕的cell, 重用这些cell,并不是重新创建cell, 这样我们就大大地提高了我们 table view的效率。

2. 重用cell的原理

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    static NSInteger count = 0;
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier ];
    if (cell == nil) {
        cell = [[UITableViewCell allocinitWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
        count++;
        NSLog(@"%d", count);
    }
    
    cell.textLabel.text = [NSString stringWithFormat:@"Row %d", indexPath.row];
    
    return cell;
}



so 大家可以理解的是,当我们启动程序的时候,我们创建了这一屏幕个数的cell, 就是11个cell,
然后等我们滑动一下,第12个cell被创建了,我们继续滑动,就没有然后了,创建cell的数量就停在12个上面。

通过上面的现象我们就可以理解到cell,  当我们创建了tableview 和 加载第一屏的cell时候,我们就创建了一个内部的队列用来管理可以重用的cell, 像上图一样,当我们滑动出row11时候,这时候我们的重用队列里面没有可以重用的cell, 我们就只有重新再创建一个cell出来,然后把已经移除屏幕的cell 0加进我们的重用队列里面去。然后我们继续滑动出row 12的时候,这时候我们去查看我们的重用队列,发现里面有可以重用的cell 0, 这时候我们就把cell 0 重用来作为cell 12, 这样就完成了,我们对cell 的重用。

为了验证我们的结论,就让我们来调试一下代码,看看是怎么回事吧,
所以我们在tableView:cellForRowAtIndexPath: 每次创建cell的时候打上断点,然后我们发现cell 0 被创建出来了,address是 0x0f608530,  这时候我们发现了tableview 里面有个成员叫做
_reusableTableCells的dictionary, 为什么是个dictionary, 因为我们可能有多种 cell identifier 可能需要重用。当我们没有滑动时候,这时候_reusableTableCells没有key.

当我们滑动时候,这时候tableView:cellForRowAtIndexPath: 又被调用,这时候_reusableTableCells 里面还是没有可以重用的,所以我们只能再创建出cell 11来,当第0个cell 被完全移除屏幕的时候,这时候我们的_reusableTableCells 把cell 0 加进来,当我们滑动出cell 12的时候,我们就可以发现,这时候我们_reusableTableCells里面有东西了,然后我们通过dequeueReusableCellWithIdentifier 把它取出来时,这话cell的地址就就是我们cell 0, 0x0f608530.
这就是iOS tableView 重用cell的原理。












Friday 7 June 2013

iOS Load and initialize


+(void)load 是用来干什么的?

      无论是否一个class 或者是一个category 静态或者是动态的加载进来的时候,都会调用load 方法。
      说白了,就是当你申明了一个class,加载进工程的时候,即使你没有用到它,这个class的load 方法已经被调用了。

#import "BaseObject.h"

@implementation BaseObject

+ (void)load {
    NSLog(@"BaseObject, %s", __FUNCTION__);    //不应该在call [super load] 在load 方法里面。
}
@end

在我的工程里,我也没有用它,但是这个BaseObject 的load 方法也被调用了。

通常当一个class有多个category的时候,如果有2个category 都实现了同一个方法的时候,只会有一个category的方法会被调用,另外一个category的方法就不会。
但是对于load就是个特例了,每个category对于一个class的load方法只要实现了,都会被调用。
当我们用+load()方法时候,是没有autorelease pool 在这里的,所以如果需要用autorelease 的话,我们就要自己设置为autorelease pool 来管理objects.

那我们一般会+load()方法里面做什么呢?
总结了下用法,主要还是做一些预处理的事情,例如经典的wrapper class 用我们的实现替换原有的实现就可以在+load()方法里面。

+ (void)load
{
    Method original, swizzled;
    
    original = class_getInstanceMethod(self, @selector(synchronize));
    swizzled = class_getInstanceMethod(self, @selector(swizzled_synchronize));
    method_exchangeImplementations(original, swizzled);
}

+(void)initialize 是用来干什么的?
       当我们第一次对一个Object 发消息的时候,  +initialize()就被调用起来了, 当然+load() 方法 我们不能算作第一调用。
同load不一样的是,子类的initialize 会触发父类的initialize,  当子类没有实现initialize的时候,他就会去用父类的initialize.

@implementation BaseObject

+ (void)initialize {
    NSLog(@"%@, %s", [self class], __FUNCTION__);
}
@end


@implementation RChildObject
@end


当我们去向RChildObject 发送事件的时候,我们就会发现,输出结果是这样的
BaseObject, +[BaseObject initialize]
RChildObject, +[BaseObject initialize]

不像+load()方法那样,在调用+initialize()的时候,我们已经有了context了,所以autorelease pool 就可以用了。

与init的不同,+initialize()是针对于class而言的,所以不管你实例化多少个实例,这个initialize只会被调用一次。
    RChildObject *objc1 = [[RChildObject alloc] init];
    RChildObject *objc2 = [[RChildObject alloc] init];
    RChildObject *objc3 = [[RChildObject alloc] init];
只会有一次输出
RChildObject, +[BaseObject initialize]

因为这样的特性,我们就可以用+initialize() 来完成我们的singleton,因为+initialize() 对于class而言只会被调用一次,并且是thread-safe.

static MySingleton *sharedSingleton;

+ (void)initialize
{
    static BOOL initialized = NO;
    if(!initialized)
    {
        initialized = YES;
        sharedSingleton = [[MySingleton alloc] init];
    }
}

这个+initialize()只会被调用一次,那为什么会加这个BOOL变量呢?
原因就是前面说的那样,如果有个subclass 没有实现+initialize()方法,那么父类的+initialize()就会调用,如果这里不加判断,就会出现泄露的问题, 如果你想加变量,你也可以这样来判断
self == [xxx class], 来判断是否是我们想要的class 发过来的信息。





Sunday 28 April 2013

新的开始

      最近换了工作,接触到了新的同事,开始了新的项目,开始了新的旅程。

万事开头难,真的不是盖的,总是觉得自己很不适合这个公司,总觉得自己的压力大感觉挺不过去了,问了些前辈的,都说才开始进去,可能不适应这些。后来慢慢发现却是自己真的懒了,怕了,觉得输不起了,太多的现实冲击着我,比自己年轻的同事却比自己懂得更多,更加有潜力,比自己大点的同事,已经功成名就了,自己还在为跳槽了那点破事忙着,心态慢慢觉得变态了,甚至有时还问自己爱编程不?

这时候想起了乔帮主的语言

  • 你的時間有限,所以不要浪費時間活在別人的生活裡。
Your time is limited, so don't waste it living someone else's life.

确实呀,我的时间本来就不多了,为什么要去想这么多呢,为什么这么care人家呢,我就是我呀,而且我真的喜欢编程,我希望10年后,告诉自己太开心了又编了10年程序。。

自己在编程方面的喜欢的太多了,技术是层出不穷,感觉自己毕业到现在3年里,追过很多技术,但是自己的成就完全不能提。
做过一些小东西,就拿ruby来说,弄了rails小网站,然后就没有然后,看了些linux的东西,然后也没有然后了。。 自己本来是做Mobile Application开发的,感觉在Mobile方面也没有贡献,觉得自己专注真的有点不够,但是自己又觉得自己是什么天才,可以内外兼修,现在发觉不够的是自己时间。
感觉自己是时候要专注了,专注一个方向做好,做精对于现在的我才是王道。
为了自己最喜欢的linux, 决定制定个操作系统的学习计划,坚持到底是走下去,其他的爱好先再旁边站站,maybe 有一天我真的能成为kernel 核心开发人员。
但是为了工作,工作事情也要雄起呀,决定对于Mobile Application做个总结,希望我能在两者之前做到很好的平衡,来成就自己的未来。

本文献给那些向我这样遇到矛盾的人们,希望能给你们带来一些帮助,希望跟大家多多交流。


Regrecall

Tuesday 19 March 2013

Improve your skills with github and stackoverflow

     I learned iOS one year ago,  started with one famous iOS book  iOS Programming: The Big Nerd Ranch Guide. For an entire year, I'm from an newbie to one that can develop some easy apps, but I think I encounter a bottleneck, I don't know how to improve my skills about iOS, even I develop another app, almost will copy some old things from Previous project, there's no new skills learned.

Fortunately, I found some ways to improve my skills, it's reading code and reading questions.

1.    Learning from Github

There are many open source projects hosted in the Github,  there is no secret in front of you.
Recently I learned one project QuickDialog,  it's used to help you create the forms with multiple text fields, or with thousands items.
It has one function, that can load the json file, and create the view according to the json file, make me understand how magic about the NSClassFromString.

When you init the root element with the json file,  it would parse the json file to the NSDictionary, and use the valueForKey(), get the value of the key, then use the NSClassFromString to construct the class which you type in the json file.

   "sections": [
                 {   "title":"password form", "footer":"You first use this app, please set your password.",
            "elements": [
                {   "type":"QEntryElement", "title":"Password","placeholder":"Password", "secureTextEntry":true, "bind":"textValue:password","key":"password"},
                {   "type":"QEntryElement", "title":"Confirm Password", "placeholder":"Password", "secureTextEntry":true, "bind":"textValue:confirmedPassword"}
                        ]
        },

when it found the "type" key, it will use  NSClassFromString  to construct the QEntryElement instance put in the root element tree.
these primary code about using the dictionary to constrcu the element.
    QSection *sect = nil;
    if ([obj valueForKey:[NSString stringWithFormat:@"type"]]!=nil){
        sect = [[NSClassFromString([obj valueForKey:[NSString stringWithFormat:@"type"]]) alloc] init];
    } else {
        sect = [[QSection alloc] init];
    }
    [self updateObject:sect withPropertiesFrom:obj];
    [root addSection:sect];
    for (id element in (NSArray *)[obj valueForKey:[NSString stringWithFormat:@"elements"]]){
       [sect addElement:[self buildElementWithObject:element] ];
    }


maybe before you used NSClassFromString to construct the class in the dynamic, but this is also awesome, it make me know the basic knowledge to config something or plug-in something from files.

Another skill (Key-Value Observing) from the project Kal,
In the Kal, there's  the KalLogic class holds the data about selected date and previous month and following month and so on.
It's the core datasource of the KalView which contains multiple subviews, so how it solve problem when view A changed, view B should change  according to view A in the KalView.

It use the Key-Value Observing, the KalView addObserver about the objects changed,  like the KalLogic changed the date, then KalView
Observing these change, then update the some subviews's frame and shown content of subvirew.

[logic addObserver:self forKeyPath:@"selectedMonthNameAndYear" options:NSKeyValueObservingOptionNew context:NULL];

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (object == gridView && [keyPath isEqualToString:@"frame"]) {
        CGFloat gridBottom = gridView.top + gridView.height;
        CGRect frame = tableView.frame;
        frame.origin.y = gridBottom;
        frame.size.height = tableView.superview.height - gridBottom;
        tableView.frame = frame;
        shadowView.top = gridBottom;
       
    } else if ([keyPath isEqualToString:@"selectedMonthNameAndYear"]) {
        NSDate * date = [change objectForKey:NSKeyValueChangeNewKey];
        [self.delegate updateYearDate:date];
   }
}

The KalLogic also have something we should learn:
implementing a class method that follows the naming convention keyPathsForValuesAffecting<Key>, where <Key> is the name of the attribute (first letter capitalized) that is dependent on the values.

+ (NSSet *)keyPathsForValuesAffectingSelectedMonthNameAndYear
{
    return [NSSet setWithObjects:@"baseDate", nil];
}

- (NSDate *)selectedMonthNameAndYear;
{
    return self.baseDate;
}


So when the baseDate changed, then the observer for the key path is @"selectedMonthNameAndYear" will be notified, and from the apple document, this methods normally allow you want to be notified when value A or Value B changed, like

+ (NSSet *)keyPathsForValuesAffectingFullName {

    return [NSSet setWithObjects:@"lastName", @"firstName", nil];

}


An application observing the fullName property must be notified when either the firstName or lastName properties change, as they affect the value of the property.

2.    Learning from Stack overflow

The core idea about learning from Stack overflow is answer question and read question,
These are my methods about Learning,
a.    First  you must see the votes about your interesting topic,  you can know something lost in your knowledge,
Like  me I learned  how to migrate to iPhone 5 app,    check the iOS version, one answer is so awesome, has a very nice marco

b.    After read so many question, maybe you want to answer someone, so you can answer the newest or unanswered question now.

Not Only iOS, I think many programming skill could use these two methods(read code and read questions) to improve,  If you have another
Methods, please tell me, I will be very appreciate.



Monday 27 August 2012

Share Folder between Mac OS and Ubuntu in VirtualBox

Last night, I want to share the unp(UNIX network programming) source code between my host Mac OS and Ubuntu in the Virtual Box.
So I summarize some steps:

 Mac OS : Mountain Lion
 Virtual Box: 4.1.20
 Ubuntu: 12.04

1.      setup the share folder in the Mac OS.
         Choose in the VirtualBox's men:  settings -> Shared Folders.
        

2.  Add the new shared folder button
     Choose the Folder Path e.g /User/reg/xxxx
     and You should give the name for it, which will be used in the mount in the Ubuntu
     Select Make permanent  -  if you would like to persist this shared folder definition





3.Now work in the Ubuntu in the Virtual Box
 
 a.   Create the folder to be shared folder mount the Mac OS Shared folder.
   e.g  ~/share
 

  b.   get you id
       
       id
    
  c. Mount the shared folder
     
    sudo mount -t vboxsf -o uid=USER_ID Mac_OS_Shared_Folder_Name Ubuntu_Shared_Name 

  
   e.g.


    sudo mount -t vboxsf -o uid=1000 share ~/share 
 
 IF you meant any issues about vboxsf 
 e.g.
    mount.xboxsf failed no such device

make sure you installed the Virtual Box's Guest Additions
  
The steps about install Guest Additions

   Choose the Virtual Box VM
   Devices ----> Install Guest Additions

   Now the Virtual Box will help you install it,
   if not, you can enter the directory in the Ubuntu  /media/VBOXADDITIONS_xxx
   run the VBoxLinuxAdditions.run


  Now make sure the vboxsf module is right
  No error to run the modprobe vboxsf

4. Now you can use the shared folder.



Others about the  UNP
if you run the ./daytimecocli 127.0.0.1

connect error: Connection refused

you May not start the daytime server

1.    Install the xinetd
       apt-get install xinetd

 2.  modify the /etc/xinetd.d/daytime,
      change the two disable yes -------> disable no

3. Now you can /etc/init.d/daytime restart











Wednesday 11 July 2012

Core Data Brief introduction

What's the Core Data?

Suppose you have the object named "People", it has two variables: name which is the string type, age which is the int type.

class People
{
      public string name;
      public int age;
}

Now I want to these people to persistent, so I use a "place" to store my people.
 No matter which will do these steps:

1.  define the people class.
2.  make one "place" to store it. (xml, binary, sqlite as you like)
3.  make the relation the data from the file with the people class. It can help you map the data to people, and map the people to data.

and so on.....

For the developer, maybe you only care the object, which you can delete, create, update, you don't care how it persistence, the style of it store, how it map to you object...
you only want to change the object, it auto changes in the persistence, you create the object, it auto insert the data in the persistence, you delete the object, it auto delete the data in persistence.

So the Core data help you.
Core Data describes data with a high level data model expressed in terms of entities and their relationships plus fetch request that retrieve entities meeting specific criteria.
Code can retrieve and manipulate this data on a purely object level without having to worry about the details of storage and retrieval.




Let's use this.
1.
First we will create the Data Model to store our "object"





 Choose the Data Model, and name it as you like.


2.
Then we will has the *.xcdatamodel in our project, its home of our object.




And it has three components:  ENTITIES, FETCH REQUESTS, CONFIGURATIONS.

The ENTITY is model that mapped our purely object.
So let add our variable to the Entity. First Add the Entity,Find the Add Entity at the bottom left of the window and click it. A new Entity will appear in the list of entities in the lefthand table, change its name.

 Then you click + button in the Attribute section and edit the Attribute and Type values:




Then we finish the persistence of our data, now we should make our object to connect to these entity. We used the NSManagedObject to do this.



Now we can finish our preparatory work. we want to use these object, we must follow Core Data stack.


Don't be afraid!
Let me show what stack do.

First: We must find our entity. Yep, it's in the file (*.xcdatamodel file that created).
So we use the Managed Object Model.

A managed object model that describes the entities in the stores.

NSManagedObjectModel * managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];

Then: There is another component to connect your object to the persistence data, this is the Persistent Store Coordinator.

A persistent store coordinator that aggregates all the stores, and context used it to persistence the object graph, and retrieve the entity information.



 NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];

it initializes with model, it must know the entity you want to store and the entity's information.

Next: We will find a place to store your entity by your type. so you must tell persistent object store coordinator, you have a store.

A persistent object store that maps between records in the store and objects in your application.

    NSString *path = [self storePath];
    NSURL *storeURL = [NSURL fileURLWithPath:path];
   
    NSError *error = nil;
   
    if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
        [NSException raise:@"Open failed" format:@"Reason: %@", [error localizedDescription]];
    }

We give a path to store our entity, and the type for our persistence.


Final:  We must use something to manipulate our object,  to connect with the coordinator.
A managed object context that provides a scratch pad for managed objects. 

    NSManagedObejctContext* context = [[NSManagedObjectContext alloc] init];
    [context setPersistentStoreCoordinator:psc];


We general communicate with NSManagedObjectContext.
We can Insert the New entity to the store.

    People *e = [NSEntityDescription insertNewObjectForEntityForName:@"People" inManagedObjectContext:context];
   
    NSError *error = nil;
    [context save:&error];

query the entity in the store

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"People" inManagedObjectContext:[self managedObjectContext]];
[fetchRequest setEntity:entity];
 
 NSError *error = nil;
NSMutableArray *mutableFetchResults = [[self.managedObjectContext executeFetchRequest:fetchRequest error:&error] mutableCopy];
if (mutableFetchResults == nil) 
}
 

delete the entity in the store
 
NSManagedObject *eventToDelete = [eventsArray objectAtIndex:indexPath.row]; 
[managedObjectContext deleteObject:eventToDelete]; 
 
 
 We can get More information from apple website, I just make a brief introducation.
 Hope you like. 
 
 Reference Link:
 
 wiki: http://en.wikipedia.org/wiki/Core_Data 

 apple: http://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/CoreData/cdProgrammingGuide.html







Monday 25 June 2012

State Pattern

What's the State Pattern?

In the Design Pattern:
Allow an object to alter its behavior when its internal state changes. The will appear to change to its class.



File:State Design Pattern UML Class Diagram.svg 



From this UML image, all the request() function calls the state.handle(), the Context don't care the which state is, just call the state.handle(). IF you not use the state pattern, you maybe implement the request() like

     switch():
           case ConcreteStateA:
                  dosomething();
           case ConcreteStateB:
                  dosomething();
when have more than one functions which implement must based on state choose, so I have the many switch case, one day you want to remove one state or add one state, you must modify all the functions which has this state choose. That is very crazy.

For example, the Mars Rovers problem:
the mars rover at the mars surface(like Cartesian Coordinate(x,y)), it has two the actions, turn left and move, and the four direction it faced, south , north, east, west.

so image the original place is (1, 5, N) means the mars rover at
coordinate (1, 5), face the North direction.
when we give the turn left command, it will become (1, 5, W), (very useful Hint from my geography teacher, In the map, up->north, down->south, left->west, right->east ).

when we give the move command, it will become (0, 5, W), the x -1, move the negative direction of x.

So You maybe have the idea for it about state pattern.

Class Rover
{
        State s;
        int x;
        int y;
        turnleft(Rover r){
             s.handleturnleft(this);     // the turnleft function call state handleturnleft function.
        }
        move(Rover r){
              s.handlemove(this);      // the move function call state handlemove function.
         }
        setState(State s){
               this.s = s;
         }
}

interface State
{
       handleturnleft(Rover r);

      handlemove(Rover r);
}

and we have the four the state inherit from State.   Give the NorthState implemention.

class NorthState : State
{
       handleturnleft(Rover r){
               r.setState(new WestState());
       }

      handlemove(Rover r){
             r. y++
      };

}


Mar Rover at (1, 5, N), and send the turn left command what happend?


the Rover->turnleft().

and the Rover's state change from NorthState to WestState.

We don't have the State choose at turnleft(, these hide in the state class,  the NorthState
knows the it turn left, will become the WestState, and It move, make the Rover.y + 1. We have no conditional choose in the function, it's very distinct.

You maybe know where use the State pattern

1.  An object's behavior depends on its state, and it must change its behavior run-time depending on that state.

2.  Operations have large, multiparty conditional statements that depend on the object's state.

The State pattern puts each branch of the conditional in a separate class. This lets you treat the object's the object's state as an object in its own right that vary independently from other objects. 

State pattern puts all behavior associated with a particular state into one object. Because all state specific code lives in a State subclass, new states and transitions can be added easily by defined new subclass. The State class is more easy to share between other classes.


Reference links:

1. Design Pattern Book (GOF)
2. http://en.wikipedia.org/wiki/State_pattern