2014年4月29日 星期二

Core Data 概念



The NSFetchedResultsController is a type of controller provided by the Core Data framework that helps manage results from queries. 
NSFetchedResultsController幫我們管理我們要做的查詢
NSManagedObjectContext is a handle to the application’s persistent store that provides a context, or environment, in which the managed objects can exist. 
NSManagedObjectContext 幫我們產生一個存放managed objects的環境,並幫我們處理與persistent store之間的溝通。

//Sample code

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:
self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Master"]; 
aFetchedResultsController.delegate = self; 
self.fetchedResultsController = aFetchedResultsController; 
我們來了解一下cacheName這個參數的用法:
如果你不想要cache查詢結果,那直接傳nil給他就好。
如果你傳了cacheName給它,那代表它會在下次搜尋之前,先去檢查上一次的cache有沒有相同的值。
如果有找到相同的查詢,就不進行查詢直接取用它;如果不相同,就刪除上次的cache,然後存放這次的查詢結果供下次使用。


the persistent store 通常在操作 SQLite database
而The managed object model 是persistent store的抽象邏輯表現層。


Calling the getter for the delegate’s managedObjectContext starts a chain reaction in which
-(NSManagedObjectContext *)managedObjectContext, calls -(NSPersistentStoreCoordinator *)persistentStoreCoordinator and then in turns calls -(NSManagedObjectModel *)managedObjectModel. The call to managedObjectContext therefore initializes the entire Core Data stack and readies Core Data for use. 

//Delete時,有四個規則
  1. No action 什麼都不做:Does nothing and lets the related objects think the parent object still exists.
  2. Nullify 把每個相關連的物件的父物件設為nil:For each related object, sets the parent object property to null. 
  3. Cascade 把所有相關的物件也一併刪除:Deletes each related object.
  4. Deny 如果仍有相關物件,則禁止delete的動作:Prevents the parent object from being deleted if there is at least one related object. 

//NSFetchedResultsController是用來給UITableView使用的
You use a fetched results controller to efficiently manage the results returned from a Core Data fetch request to provide data for a UITableView object.

//讀取資料
- (NSArray *) readDataWithEntityName:(NSString *)entityName andPredicate:(NSPredicate *)predicate sortDescriptor:(NSString *)sortDescriptorString asceding:(BOOL)isAsceding{
    
    NSManagedObjectContext *context = [_coreData managedObjectContext];
    NSEntityDescription *entityDescription = [NSEntityDescription entityForName:entityName inManagedObjectContext:context];
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:entityDescription];
    
    // Set example predicate and sort orderings...
    [request setPredicate:predicate];
    
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]
                                        initWithKey:sortDescriptorString ascending:isAsceding];
    [request setSortDescriptors:@[sortDescriptor]];
    
    NSError *error;
    NSArray *array = [context executeFetchRequest:request error:&error];
    if (array == nil)
    {
        // Deal with error...
    }
    return array;

}

#pragma mark Save
- (void)saveContext
{
    NSError *error = nil;
    NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
    if (managedObjectContext != nil) {
        if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
            // Replace this implementation with code to handle the error appropriately.
            // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }
}

#pragma mark Add
- (void) addNewRecordIntoEntityByName:(NSString *)entityName newObjects:(NSArray *)objects{
    
    NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:_managedObjectContext];
    
    // If appropriate, configure the new managed object.
    // Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template.
    for (NSDictionary *objectInfo in objects) {
        [objectInfo enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
            [newManagedObject setValue:obj forKey:key];
        }];
    }
    
    
    // Save the context.
    NSError *error = nil;
    if (![_managedObjectContext save:&error]) {
        // Replace this implementation with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }
}
#pragma mark Delete
//刪除指定物件
-(void) deleteRecord:(NSManagedObject *)managedObject{
    
    [_managedObjectContext deleteObject:managedObject];
    
    NSError *error = nil;
    if (![_managedObjectContext save:&error]) {
        // Replace this implementation with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }
}

//恢復上一步
-(void) undoRecord{
    
    [_managedObjectContext.undoManager undo];
    
    NSError *error = nil;
    if (![_managedObjectContext save:&error]) {
        // Replace this implementation with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }
}

//恢復下一步
-(void) redoRecord{
    
    [_managedObjectContext.undoManager redo];
    
    NSError *error = nil;
    if (![_managedObjectContext save:&error]) {
        // Replace this implementation with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }

}

2014年4月28日 星期一

imageNamed V.S. imageWithContentsOfFile

imageNamed

  • 說明

會先檢查cache中有沒有圖片,有的話直接取用,沒有的話,就把圖片放入cache中不釋放。

  • 範例

UIImage *image = [UIImage imageNamed:@"imageName.png"];


imageWithContentsOfFile

  • 說明

不檢查cache中有沒有圖片,每次都直接讀取圖片檔。

  • 範例

NSString *imageName = [NSString stringWithString:@"imageName.png"];
[UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%@/%@", [[NSBundle mainBundle] bundlePath], imageName]];

2014年4月27日 星期日

指標與記憶體(Pointers and Memory)

有四種指標變數,對應三種記憶體

一、Static/Global
  1. Static:作用範圍:被宣告的function中。生命週期:等同於這支應用程式。
  2. Global:作用範圍:所有的function都可存取。生命週期:等同於這支應用程式。
二、Automatic(Local)
作用範圍:被宣告的function中。生命週期:在這個function被呼叫的時候。
三、Dynamic
作用範圍:由指向這塊heap的指標所決定。生命週期:直到這塊memory被釋放。 

//關於Static的作用範圍,Objective-C跟C有些不同,請參考下列資料
Variable storage class specifiers are used when declaring a variable to give the compiler information about how a variable is likely to be used and accessed within the program being compiled. So far in this chapter we have actually already looked at two storage class specifiers in the form of extern and static. A full list of variable storage class specifiers supported by Objective-C is as follows:
  • extern - Specifies that the variable name is referencing a global variable specified in a different source file to the current file.
  • static - Specifies that the variable is to be accessible only within the scope of the current source file.
  • auto - The default value for variable declarations. Specifies the variable is to be local or global depending on where the declaration is made within the code. Since this is the default setting this specifier is rarely, if ever, used.
  • const - Declares a variable as being read-only. In other words, specifies that once the variable has been assigned a value, that value will not be subsequently changed.
  • volatile - Specifies that the value assigned to a variable will be changed in subsequent code. The default behavior for variable declarations.

2014年4月24日 星期四

NSArray排序(sorting)

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]initWithKey:@"recordDate" ascending:YES];
        NSArray *sortedArray = [queryArray sortedArrayUsingDescriptors:@[sortDescriptor]];

這裡要注意SortDescriptors是陣列,他會以第0個item為主,如果遇到二筆以上同順序的,才會以之後的item來排列。

如何在CoreData中做SUM運算

//節錄自http://stackoverflow.com/questions/8680975/iphone-core-data-how-to-get-sum-of-values-from-db
There are two ways to solve this problem:
first - get your data to NSSet or NSArray and use @sum operator:
//assume that `pens` are NSArray of Pen
NSNumber *countSum=[pens valueForKeyPath:@"@sum.count"];
second is using specific fetch for specific value with added NSExpressionDescription with a sum. This way is harder but better for larger db's


//以下為使用magicrecords
NSNumber *countSum = [RecordEntity MR_aggregateOperation:@"sum:" onAttribute:@"steps" withPredicate:nil];

NSLog(@"算總和 : %@",countSum);

nil & NULL

節錄自http://nshipster.com/nil/
SymbolValueMeaning
NULL(void *)0literal null value for C pointers
nil(id)0literal null value for Objective-C objects
Nil(Class)0literal null value for Objective-C classes
NSNull[NSNull null]singleton object used to represent null

TI-CC2541 PARAMETERS



android
max:39
min:39
latency:0
timeout:700

iOS
max:24
min:24
latency:0
timeout:72

//iOS限制如下,且只接受在connection時設定,不接受動態調整。
 底下是我們目前量測到的結果:

目前Firmware參數
調整後參數
Android參數
Connection Interval
80
80
39
Slave Latency
0
10
0
Supervision Timeout
2000
700
700
平均耗電流
232 uA
18 uA
496 uA
耗電量 (a X 30S)/3600
0.00193 mAh
0.00015 mAh
0.00413 mAh

https://developer.apple.com/hardwaredrivers/BluetoothDesignGuidelines.pdf
3.6 Connection Parameters
The accessory is responsible for the connection parameters used for the Low Energy connection. The accessory should request connection parameters appropriate for its use case by sending an L2CAP Connection Parameter Update Request at the appropriate time. See the Bluetooth 4.0 specification, Volume 3, Part A, Section 4.20 for details. The connection parameter request may be rejected if it does not comply with all of these rules:
Interval Max * (Slave Latency + 1) 2 seconds Interval Min 20 ms
Interval Min + 20 ms
Interval Max Slave Latency 4 connSupervisionTimeout 6 seconds
Interval Max * (Slave Latency + 1) * 3 < connSupervisionTimeout
If Bluetooth Low Energy HID is one of the connected services of an accessory, connection interval down to 11.25 ms may be accepted by the Apple product.
The Apple product will not read or use the parameters in the Peripheral Preferred Connection Parameters characteristic. See the Bluetooth 4.0 specification, Volume 3, Part C, Section 12.5.