domenica 9 settembre 2012

Core Data and Memory Management using large objects - some tips

Ok, let's talk about Core Data.
Core Data is not simply a Database, but an entire framework that takes care about the entire lifecycle of your objects, even ensuring persistency (which means: you can save your objects state and keep it between an app launch and another).
When you decide to manage an object using Core Data (creating an instance of NSManagedObject), you can fetch it from the persistent store, use predicate to filter objects, add, update, delete your objects and then save again the context. You don't have to manage the database select/insert/update.
Remember:

1-fetch or create the objects
2- do your stuff (change properties, read, ecc)
3- save the context

All simple and useful.
But how about memory management? In some cases, it could be not simple to manage memory.
Core Data use a caching system that should do all the work "automagically"...but it won't work if you don't take some care.

We can use an example.
We want to use Core Data to store a simple web image cache. Create an entity:

Entity name: ImageCache
Entity properties:
    NSString *url;
    NSData *imageData;

Store a lot of image inside it (like, for example, 1GB), and try to fetch all: your app will crash ;-)
Sure, you can filter images and fetch only the image you need, but why do this?

If you have read Core Data documentation, you should know about Faulting: when you fetch an item, the item properties are not fully loaded into memory. The property contains only a "link" to the real data. When you try to access it the first time, Core Data transparently loads it from the persistent store (or  from the cache, if the item is cached).
For this reason, you should not care too much about memory, because if you fetch all properties, image data should be loaded only at the first access to the property imageCacheInstance.imageData

Now, come back to our test: if you try to fetch all images, the app will crash. Why?
Let's open Instruments, write some NSLog lines and check.
When you fetch your data, the imageData property is correctly faulted (you can check calling isFault method). But all data are loaded into memory.

First rule: don't store your data inside a "top level object"
Change your Core Data Model to something like this:

Entity 1 Name: ImageCache
Entity 1 Properties:
    NSString *url;
    id imageDataReference;

Entity 2 Name: ImageData
Entity 2 Properties:
    NSData *image;
    id imageDataReference;

Two entities, one 1:1 relation.
Now, if you fetch all ImageCache values, you should see into memory only few data.
Then, if you access to imageCacheInstance.imageDataReference.image, the ImageData instance is faulted and the data loaded from the persistent store into memory.

When you have finished, you can fault again the ImageData instance and free memory. Simply send this message (to each ImageCache instance):

[_managedObjectContext refreshObject:imageCacheInstance mergeChanges:NO];

note: don't call this on the ImageData Instance, because the cache will not be freed (even if the object will be faulted, all the data will remain in memory)
Call it on the "father" object and all should work correctly.

domenica 30 gennaio 2011

Backup completo di Gmail (incluse "etichette") con imapsync

Finalmente!!!
Dopo tanto cercare ho trovato un sistema:

- comodo
- rapido
- facilmente automatizzabile (su linux)
- "completo"

per eseguire il backup della mia gmail.
Lo scopo era ottenere un backup completo di TUTTA la gerarchia di cartelle imap (che gmail chiama etichette).
Il tutto è facilmente ottenibile con imapsync
http://www.linux-france.org/prj/imapsync/
(gratuito, ma con sorgenti a pagamento)

Una volta installato (su Ubuntu 10.04 è disponibile tramite apt-get install imapsync) è sufficiente

- avere un server imap su cui depositare i backup (ne ho installato uno con postfix sul mio serverino ubuntu)
- dare un comando di questo tipo:

imapsync --host1 imap.gmail.com --port1 993 --ssl1 --user1 nomeutente@gmail.com --password1 password --host2 localhost --user2 nomeutente@serverbackup.com --password2 password

a volte, può esser necessario aggiungere

--noauthmd5 --allowsizemismatch --exclude GMAIL

Per info, chiedete pure in mail :)
Ciao!

iPhone 4, l'antennagate esiste ed è evidente

Ne ho lette di tutti i colori a riguardo....
Non sarà un'opinione autorevole :) ma riporto la mia personale esperienza.

Ho l'iPhone 4 da fine agosto 2010, acquistato poco dopo la sua uscita.

Prima avevo un 3gs, e la mia ragazza ha tutt'ora un 3gs.

Ci sono due posti, in cui passo buona parte del mio tempo, in cui il segnale del mio operatore (Vodafone) è molto basso in 3g.
Non ho mai usato cover/bumper, e in questi posti mi capita, nel 90% dei casi, che il segnale passi da 3g a gprs non appena prendo in mano il telefono.
Il 3gs della mia ragazza è, negli stessi posti, quasi sempre in gprs.
Usare il 4 è una tortura, perchè continua a switchare da 3g a gprs, con conseguenti buchi, disconnessioni.
I problemi si sentono nella navigazione internet, la fonia è fortunatamente "ok" (mai caduta).
Il 3gs è già più utilizzabile, "a rilento", perchè non effettua continui passaggi da 2g a 3g.

Un paio di giorni fa ho ricevuto, in regalo dalla mia ragazza, 2 bumper (per la cronaca, uno verde e uno nero, quello verde è proprio bello :)

A questo punto posso dire che il fenomeno dell'antennagate esiste:

In entrambi i posti "di test", *non passa più* in gprs, rimane costantemente in 3g, e ho visto qualche volta anche 2 tacche.
La navigazione e lo speedtest, sempre in 3g, sono visibilmente migliorati

In generale, l'iPhone 4 ha una ricezione media superiore rispetto al 3gs, che però cala vistosamente quando viene tenuto in mano (eh si, io lo tengo nel modo "giusto", non sono mancino :D). Con il bumper il fenomeno svanisce, e si nota la differenza.

Anche senza bumper il telefono è perfettamente usabile, ma il suo utilizzo diventa un po' frustrante in quei luoghi dove il segnale 3g è debole. In quest'ultimo caso, un bumper fa miracoli :)

domenica 23 gennaio 2011

Huawei Ideos, carino

Premessa: questa non è una recensione, ma una serie di appunti sparsi e pensieri scritta in 3 minuti.
Se non vi piace, affari vostri :P

Ieri, in preda a una crisi di "sete tecnologica", decido di acquistare un cellulare :D
Piccola nota: da quando sono nato ad oggi (26 anni), ho posseduto circa 30 telefoni cellulari diversi.
Mi sono "tranquillizzato" da quando è uscito iPhone, che è il mio cellulare principale da qualche anno (li ho avuti tutti, dal 2g al 4 che ho ora :P)
Ho, però, anche un secondo cellulare, dove tengo la sim aziendale.

Fino a ieri il cellulare "di servizio" era un ottimo Nokia E51, che però si stava avvicinando alla tomba (si sentiva bassissimo, il tasto di accensione era rotto e richiedeva l'unghia del mignolino della mia ragazza per poterlo premere...ecc ecc)

Comunque, dicevo: vedo questo Ideos, in offerta con Vodafone a 99 euro, e mi dico: perchè no??

Con 99 euro ti danno:

- una sim con 5 euro, che chissenefrega ma comunque c'è
- un terminale leggero, touchscreen (non multitouch)
- Android 2.2 (motivo per cui l'ho acquistato, volevo provare Android :)
- HSDPA, Wifi, Bluetooth, tethering via wifi, riconoscimento vocale, navigatore di google ecc
- Fotocamera da BOHmegapixel

Insomma, dato che si tratta di un cellulare "di servizio", ho cercato qualcosa di economico ma funzionale...e devo dire che sono rimasto piacevolmente sorpreso.
Con 99 euro si può comprare un terminale Android aggiornato e con una serie di caratteristiche di tutto rispetto.
Complimenti a Huawei (che finalmente ho capito come si pronuncia :D)

Avaya Devlink, Visual Basic 6.0 e VB.Net 2010

Premessa: non sono un programmatore :) ma ogni tanto mi capita di metter mano a codice sorgente...per necessità!
Necessità che oggi si è presentata: il nostro programmatore deve creare una componente software (servizio di Windows) che si occupa di ricevere dati da una centrale telefonica Avaya IPOffice, per poi elaborarli.
I dati in questione, ottenuti tramite una DLL chiamata DevLink (devlink.dll) proprietaria Avaya, sono relativi alle attività eseguite sulla centrale stessa (in breve, è uno stream di dati in tempo reale, che segnala l'arrivo di una chiamata, la risposta, il riaggancio ecc).

La dll, scritta in c++, e il relativo sdk, forniscono pochi ma "sufficienti" esempi. Esempi di utilizzo in c++ e in Delphi.
*Purtroppo*, noi dobbiamo utilizzarla con VB.Net :)
La DLL in questione si basa sul principio delle funzioni di callback: una volta inizializzata, è necessario chiamare una funzione DLOpen passandole vari parametri (ip del centralino, password), tra cui l'indirizzo di memoria di una funzione di callback da noi creata.
La dll richiamerà questa funzione di callback ogni volta che un dato sarà disponibile.
La callback dovrà esser pronta a ricevere vari dati, tra cui una semplice stringa di testo contenente le informazioni in questione.

Dopo due giorni di smanettamenti, il nostro programmatore è ancora in alto mare...allora decido di dargli una mano.

Non conosco VB.Net, ma ho programmato (per un breve periodo) in VB6...decido quindi di provare a integrare la dll in un ambiente conosciuto, per poi eventualmente convertire il codice.

La cosa non è affatto semplice: dopo diverse ricerche su google (e almeno una 50ina di tab aperti), scopro che per lavorare con una "unmanaged dll che utilizza funzioni callback" (che, perdonatemi, non so bene cosa voglia dire :P, ma ho capito che era il mio caso) con VB6, è estremamente arduo e spesso non porta a risultati.

Non mi perdo d'animo, e riesco a scrivere il seguente codice:

in un form (form1) con due pulsanti di test e una listbox di debug


------------------------------------------------------------------------------------------------------------------------------------


Private Sub Command1_Click() 
On Error GoTo h

Dim pbxaddress As String
Dim pbxpassword As String
Dim reserved1 As String
Dim reserver2 As String
Dim pbxhandle1 As Long
Dim valore As Long
Dim cb As Long
DoEvents
pbxaddress = "192.168.15.245"
pbxpassword = "password"
pbxhandle1 = 1
cb = GetProcAddress(AddressOf HandleCommsEvent)

DoEvents
valore = DLOpen(ByVal pbxhandle1, ByVal pbxaddress, ByVal pbxpassword, Nothing, Nothing, cb)
DoEvents

Do While True
DoEvents
Loop


Exit Sub

h:

MsgBox Err.Description


End Sub


Private Sub Command2_Click()
MsgBox DLClose(ByVal 1)
End Sub

Public Function GetProcAddress(ByVal lngAddressOf As Long) As Long
GetProcAddress = lngAddressOf
End Function

Private Sub Command3_Click()
On Error GoTo h

Dim pbxaddress As String
Dim pbxpassword As String
Dim reserved1 As String
Dim reserver2 As String
Dim pbxhandle1 As Long
Dim valore As Long
Dim cb As Long
DoEvents
cb2 = GetProcAddress(AddressOf HandleCallLogEvent)
pbxhandle1 = 1
DoEvents
valore = DLRegisterType2CallDeltas(ByVal pbxhandle1, cb2)
DoEvents
Do While True
DoEvents
Loop


Exit Sub

h:

MsgBox Err.Description
End Sub



------------------------------------------------------------------------------------------------------------------------------------


command1 apre la connessione
Command2 la chiude
command3 dice alla dll "inizia a mandarmi messaggi"



In un modulo .bas (non va bene nel codice di un form)
------------------------------------------------------------------------------------------------------------------------------------

Public Declare Function DLOpen Lib "C:\Program Files\Avaya\IP Office\DEV Link\devlink.dll" (ByVal pbxh As Long, pbx_address As Any, pbx_password As Any, reserved1 As Any, reserved2 As Any, ByVal cb As Long) As Long
Public Declare Function DLClose Lib "C:\Program Files\Avaya\IP Office\DEV Link\devlink.dll" (ByVal pbxh As Long) As Long
Public Declare Function DLRegisterType2CallDeltas Lib "C:\Program Files\Avaya\IP Office\DEV Link\devlink.dll" (ByVal pbxh As Long, ByVal cb As Long) As Long



Public Sub HandleCommsEvent(ByVal pbxh As Long, ByVal comms_evt As Long, ByVal parm1 As Long)
Form1.List1.AddItem comms_evt
End Sub

Public Sub HandleCallLogEvent(ByVal pbxh As Long, ByVal info As String)
Form1.List2.AddItem "- " + info
End Sub
------------------------------------------------------------------------------------------------------------------------------------


In particolare, ho dichiarato le 3 funzioni principali della devlink.dll e creato due callback function (una che ci comunica lo stato della connessione a ogni sua variazione, una che riceve gli eventi di chiamata)
In entrambi i casi, aggiungo il valore a una lista sul form1 (solo a scopo debug)


E' importante considerare che:

- La funzione di callback, va messa assolutamente in un modulo (standard, non di classe)
- Nella dichiarazione della DLL, i valori stringa (per c++), vanno dichiarati a Any, in quanto VB e C++ rappresentano le stringhe in modo diverso
- tutti i passaggi di parametri vanno fatti byval
- il passaggio dell'indirizzo di memoria delle funzioni di callback va fatto semplicemente passando "byval" il valore numerico dell'indirizzo di memoria della callback, ottenuto tramite la funzione "addressof"
- di conseguenza, nella dichiarazione della dll, il parametro della funzione di callback, andrà dichiarato di tipo long (ByVal cb As Long)


Il codice così descritto funziona perfettamente in VB6.
Altre difficoltà sono state però incontrate nella trasposizione in VB.NET 2010. Scriverò un po' di dettagli nei prossimi giorni, ma in particolare:

- Non si può usare AddressOf, ma è necessario usare i delegati
- i Long di VB6, corrispondono agli Integer di VB.Net 2010 (che sono a 32 bit, mentre i long di VB2010 sono a 64 bit)

Se qualcuno riscontrasse difficoltà nell'implementazione della dll in codice VB6 o VB.Net, faccia un fischio in mail ;)

Ciao!!
  

martedì 7 dicembre 2010

Benvenuti =)

Primissimo post nel mio nuovo blog! Di cosa ho intenzione di parlare? Boh! Di tutto quello che mi passa per la testa!!!

Il tema principale, sicuramente, sarà l'informatica. Durante il mio lavoro (che potrei riassumere con la parola "sistemista", ma è riduttivo :) mi trovo sempre più spesso a risolvere problemi. Sembra quasi che io abbia una *calamita* per situazioni problematiche che nessuno ha mai sperimentato o affrontato prima!
E allora, perchè non condividere le mie mirabolanti (ROTFL) scoperte?
Poi parlerò di...tutto ciò che mi passa per la testa :)

Alla prossima!!!

LombaX