CALyer memory leak - memory-leaks

I found my program has a memory leak problem in the detection of memory, the code is as follows
- (CALayer *)layerWithFrame:(CGRect)aFrame
contentGravity:(NSString *)aContentGravity
cornerRadius:(float)aRadius
doubleSided:(BOOL)aValue
{
CALayer *layer = [CALayer layer];
*****layer.frame = aFrame;*****
if (aContentGravity) {
layer.contentsGravity = aContentGravity;
}
layer.cornerRadius = aRadius;
layer.masksToBounds = YES;
layer.doubleSided = NO;
layer.contentsGravity = kCAGravityBottom;
return layer;
}
Asterisk is my program memory location,This is the use of up code:
CALayer *testLayer = [self layerWithFrame: layerRect contentGravity:kCAGravityTop cornerRadius:sublayerCornerRadius doubleSided:NO];

Related

Desktop duplication (DirectX) screen capture fails to deliver screen updates

I'm working on an application that would capture the screen through Desktop duplication APIs (using DirectX 11) (only the diff to the previous screen update) and render it on another window (The viewer might be running on another machine connected via LAN). The code is an improved version of the sample provided in MSDN. Everything works fine except the device did not give any screen update though there is one some times in the mid, that happens around 10% of the time on some machines (mostly on windows 8/8.1 machines and rarely on windows 10 machines). I tried all the possible ways to sort out this problem. Reduced the number of device resets, that provided me some what reliable output but not always work fine for 100%.
The device fails to provide an initial screen (a full screen) some times (This happens 60% of the time on all windows operating systems where Desktop duplication is supported), I came up with a work around that retried for an initial update from the device until it provides one but that too resulted in multiple issues, the device might not even give the initial screen ever.
I have already invested weeks of my efforts to fix the problem but did not figure out a proper solution and there are no forums I know that discusses these kind of issues. Any help would be appreciated.
Below is my code to get the screen diff to the previous one, init the device, populating the adapters and monitors.
Please bear with me for a very long code snippet, Thanks in advance.
To Get the screen update:
INT getChangedRegions(int timeout, rectangles &dirtyRects, std::vector <MOVE_RECT> &moveRects, UINT &rect_count, RECT ScreenRect)
{
UINT diffArea = 0;
FRAME_DATA currentFrameData;
bool isTimeOut = false;
TRY
{
m_LastErrorCode = m_DuplicationManager.GetFrame(&currentFrameData, timeout, &isTimeOut);
if(SUCCEEDED(m_LastErrorCode) && (!isTimeOut))
{
if(currentFrameData.FrameInfo.TotalMetadataBufferSize)
{
m_CurrentFrameTexture = currentFrameData.Frame;
if(currentFrameData.MoveCount)
{
DXGI_OUTDUPL_MOVE_RECT* moveRectArray = reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*> (currentFrameData.MetaData);
if (moveRectArray)
{
for(UINT index = 0; index < currentFrameData.MoveCount; index++)
{
//WebRTC
// DirectX capturer API may randomly return unmoved move_rects, which should
// be skipped to avoid unnecessary wasting of differing and encoding
// resources.
// By using testing application it2me_standalone_host_main, this check
// reduces average capture time by 0.375% (4.07 -> 4.055), and average
// encode time by 0.313% (8.042 -> 8.016) without other impacts.
if (moveRectArray[index].SourcePoint.x != moveRectArray[index].DestinationRect.left || moveRectArray[index].SourcePoint.y != moveRectArray[index].DestinationRect.top)
{
if(m_UseD3D11BitmapConversion)
{
MOVE_RECT moveRect;
moveRect.SourcePoint.x = moveRectArray[index].SourcePoint.x * m_ImageScalingFactor;
moveRect.SourcePoint.y = moveRectArray[index].SourcePoint.y * m_ImageScalingFactor;
moveRect.DestinationRect.left = moveRectArray[index].DestinationRect.left * m_ImageScalingFactor;
moveRect.DestinationRect.top = moveRectArray[index].DestinationRect.top * m_ImageScalingFactor;
moveRect.DestinationRect.bottom = moveRectArray[index].DestinationRect.bottom * m_ImageScalingFactor;
moveRect.DestinationRect.right = moveRectArray[index].DestinationRect.right * m_ImageScalingFactor;
moveRects.push_back(moveRect);
diffArea += abs((moveRect.DestinationRect.right - moveRect.DestinationRect.left) *
(moveRect.DestinationRect.bottom - moveRect.DestinationRect.top));
}
else
{
moveRects.push_back(moveRectArray[index]);
diffArea += abs((moveRectArray[index].DestinationRect.right - moveRectArray[index].DestinationRect.left) *
(moveRectArray[index].DestinationRect.bottom - moveRectArray[index].DestinationRect.top));
}
}
}
}
else
{
return -1;
}
}
if(currentFrameData.DirtyCount)
{
RECT* dirtyRectArray = reinterpret_cast<RECT*> (currentFrameData.MetaData + (currentFrameData.MoveCount * sizeof(DXGI_OUTDUPL_MOVE_RECT)));
if (!dirtyRectArray)
{
return -1;
}
rect_count = currentFrameData.DirtyCount;
for(UINT index = 0; index < rect_count; index ++)
{
if(m_UseD3D11BitmapConversion)
{
RECT dirtyRect;
dirtyRect.bottom = dirtyRectArray[index].bottom * m_ImageScalingFactor;
dirtyRect.top = dirtyRectArray[index].top * m_ImageScalingFactor;
dirtyRect.left = dirtyRectArray[index].left * m_ImageScalingFactor;
dirtyRect.right = dirtyRectArray[index].right * m_ImageScalingFactor;
diffArea += abs((dirtyRect.right - dirtyRect.left) *
(dirtyRect.bottom - dirtyRect.top));
dirtyRects.push_back(dirtyRect);
}
else
{
diffArea += abs((dirtyRectArray[index].right - dirtyRectArray[index].left) *
(dirtyRectArray[index].bottom - dirtyRectArray[index].top));
dirtyRects.push_back(dirtyRectArray[index]);
}
}
}
}
return diffArea;
}
CATCH_ALL(e)
{
LOG(CRITICAL) << _T("Exception in getChangedRegions");
}
END_CATCH_ALL
return -1;
}
Here is the code to init the device
//
// Initialize duplication interfaces
//
HRESULT cDuplicationManager::InitDupl(_In_ ID3D11Device* Device, _In_ IDXGIAdapter *_pAdapter, _In_ IDXGIOutput *_pOutput, _In_ UINT Output)
{
HRESULT hr = E_FAIL;
if(!_pOutput || !_pAdapter || !Device)
{
return hr;
}
m_OutputNumber = Output;
// Take a reference on the device
m_Device = Device;
m_Device->AddRef();
/*
// Get DXGI device
IDXGIDevice* DxgiDevice = nullptr;
HRESULT hr = m_Device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&DxgiDevice));
if (FAILED(hr))
{
return ProcessFailure(nullptr, _T("Failed to QI for DXGI Device"), _T("Error"), hr);
}
// Get DXGI adapter
IDXGIAdapter* DxgiAdapter = nullptr;
hr = DxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&DxgiAdapter));
DxgiDevice->Release();
DxgiDevice = nullptr;
if (FAILED(hr))
{
return ProcessFailure(m_Device, _T("Failed to get parent DXGI Adapter"), _T("Error"), hr);//, SystemTransitionsExpectedErrors);
}
// Get output
IDXGIOutput* DxgiOutput = nullptr;
hr = DxgiAdapter->EnumOutputs(Output, &DxgiOutput);
DxgiAdapter->Release();
DxgiAdapter = nullptr;
if (FAILED(hr))
{
return ProcessFailure(m_Device, _T("Failed to get specified output in DUPLICATIONMANAGER"), _T("Error"), hr);//, EnumOutputsExpectedErrors);
}
DxgiOutput->GetDesc(&m_OutputDesc);
IDXGIOutput1* DxgiOutput1 = nullptr;
hr = DxgiOutput->QueryInterface(__uuidof(DxgiOutput1), reinterpret_cast<void**>(&DxgiOutput1));
*/
_pOutput->GetDesc(&m_OutputDesc);
// QI for Output 1
IDXGIOutput1* DxgiOutput1 = nullptr;
hr = _pOutput->QueryInterface(__uuidof(DxgiOutput1), reinterpret_cast<void**>(&DxgiOutput1));
if (FAILED(hr))
{
return ProcessFailure(nullptr, _T("Failed to QI for DxgiOutput1 in DUPLICATIONMANAGER"), _T("Error"), hr);
}
// Create desktop duplication
hr = DxgiOutput1->DuplicateOutput(m_Device, &m_DeskDupl);
DxgiOutput1->Release();
DxgiOutput1 = nullptr;
if (FAILED(hr) || !m_DeskDupl)
{
if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE)
{
return ProcessFailure(nullptr, _T("Maximum number of applications using Desktop Duplication API"), _T("Error"), hr);
}
return ProcessFailure(m_Device, _T("Failed to get duplicate output in DUPLICATIONMANAGER"), _T("Error"), hr);//, CreateDuplicationExpectedErrors);
}
return S_OK;
}
Finally to get the current frame and difference to the previous one:
//
// Get next frame and write it into Data
//
_Success_(*Timeout == false && return == DUPL_RETURN_SUCCESS)
HRESULT cDuplicationManager::GetFrame(_Out_ FRAME_DATA* Data, int timeout, _Out_ bool* Timeout)
{
IDXGIResource* DesktopResource = nullptr;
DXGI_OUTDUPL_FRAME_INFO FrameInfo;
try
{
// Get new frame
HRESULT hr = m_DeskDupl->AcquireNextFrame(timeout, &FrameInfo, &DesktopResource);
if (hr == DXGI_ERROR_WAIT_TIMEOUT)
{
*Timeout = true;
return S_OK;
}
*Timeout = false;
if (FAILED(hr))
{
return ProcessFailure(m_Device, _T("Failed to acquire next frame in DUPLICATIONMANAGER"), _T("Error"), hr);//, FrameInfoExpectedErrors);
}
// If still holding old frame, destroy it
if (m_AcquiredDesktopImage)
{
m_AcquiredDesktopImage->Release();
m_AcquiredDesktopImage = nullptr;
}
if (DesktopResource)
{
// QI for IDXGIResource
hr = DesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&m_AcquiredDesktopImage));
DesktopResource->Release();
DesktopResource = nullptr;
}
if (FAILED(hr))
{
return ProcessFailure(nullptr, _T("Failed to QI for ID3D11Texture2D from acquired IDXGIResource in DUPLICATIONMANAGER"), _T("Error"), hr);
}
// Get metadata
if (FrameInfo.TotalMetadataBufferSize)
{
// Old buffer too small
if (FrameInfo.TotalMetadataBufferSize > m_MetaDataSize)
{
if (m_MetaDataBuffer)
{
delete [] m_MetaDataBuffer;
m_MetaDataBuffer = nullptr;
}
m_MetaDataBuffer = new (std::nothrow) BYTE[FrameInfo.TotalMetadataBufferSize];
if (!m_MetaDataBuffer)
{
m_MetaDataSize = 0;
Data->MoveCount = 0;
Data->DirtyCount = 0;
return ProcessFailure(nullptr, _T("Failed to allocate memory for metadata in DUPLICATIONMANAGER"), _T("Error"), E_OUTOFMEMORY);
}
m_MetaDataSize = FrameInfo.TotalMetadataBufferSize;
}
UINT BufSize = FrameInfo.TotalMetadataBufferSize;
// Get move rectangles
hr = m_DeskDupl->GetFrameMoveRects(BufSize, reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(m_MetaDataBuffer), &BufSize);
if (FAILED(hr))
{
Data->MoveCount = 0;
Data->DirtyCount = 0;
return ProcessFailure(nullptr, L"Failed to get frame move rects in DUPLICATIONMANAGER", L"Error", hr);//, FrameInfoExpectedErrors);
}
Data->MoveCount = BufSize / sizeof(DXGI_OUTDUPL_MOVE_RECT);
BYTE* DirtyRects = m_MetaDataBuffer + BufSize;
BufSize = FrameInfo.TotalMetadataBufferSize - BufSize;
// Get dirty rectangles
hr = m_DeskDupl->GetFrameDirtyRects(BufSize, reinterpret_cast<RECT*>(DirtyRects), &BufSize);
if (FAILED(hr))
{
Data->MoveCount = 0;
Data->DirtyCount = 0;
return ProcessFailure(nullptr, _T("Failed to get frame dirty rects in DUPLICATIONMANAGER"), _T("Error"), hr);//, FrameInfoExpectedErrors);
}
Data->DirtyCount = BufSize / sizeof(RECT);
Data->MetaData = m_MetaDataBuffer;
}
Data->Frame = m_AcquiredDesktopImage;
Data->FrameInfo = FrameInfo;
}
catch (...)
{
return S_FALSE;
}
return S_OK;
}
Update :
Failed to acquire next frame in DUPLICATIONMANAGER is getting printed whenever the device has hung (That is in the mid of streaming the screens, Ex: Continuously capturing a video and sending it to the other end)
// Get new frame
HRESULT hr = m_DeskDupl->AcquireNextFrame(timeout, &FrameInfo, &DesktopResource);
if (hr == DXGI_ERROR_WAIT_TIMEOUT)
{
*Timeout = true;
return S_OK;
}
*Timeout = false;
if (FAILED(hr))
{
return ProcessFailure(m_Device, _T("Failed to acquire next frame in DUPLICATIONMANAGER"), _T("Error"), hr);//, FrameInfoExpectedErrors);
}
here is the detailed error info :
Id3d11DuplicationManager::ProcessFailure - Error: Failed to acquire next frame in DUPLICATIONMANAGER, Detail: The keyed mutex was abandoned.
Update 2 :
I have got the error code whenever the device failed to give screen updates forever, And here is the same
Id3d11DuplicationManager::ProcessFailure - Error: Failed to get duplicate output in DUPLICATIONMANAGER, Detail: Access is denied.
The error code is E_ACCESSDENIED.
I do not understand why I am getting this error as I am running in SYSTEM mode already and the SetThreadDesktop had been executed twice (One during the init and another after detecting a failure)
This is what the explanation of the error on MSDN : E_ACCESSDENIED if the application does not have access privilege to the current desktop image. For example, only an application that runs at LOCAL_SYSTEM can access the secure desktop.
Is there anything else that would result in this kind of issue?
It's always good to check the return codes and immediately fall back to GDI or any other available screen capturing approach in case of non-recoverable errors. Retrying doesn't work most of the time for certain hardware errors like max limit reached, out of memory, device removed, etc, I learned it in a hard way. Furthermore, DirectX device takes a few iterations before producing an initial frame on rare occasions. It wouldn't be useful to retry more than 10 times, you can safely fallback or try re-initializing the device to check one more time before falling back.
Here are some basic checks to do:
Handle DXGI_ERROR_NOT_CURRENTLY_AVAILABLE error:
_pOutput->GetDesc(&m_OutputDesc);
// QI for Output 1
IDXGIOutput1* DxgiOutput1 = nullptr;
hr = _pOutput->QueryInterface(__uuidof(DxgiOutput1), reinterpret_cast<void**>(&DxgiOutput1));
if (FAILED(hr))
{
return ProcessFailure(nullptr, _T("Failed to QI for DxgiOutput1 in DUPLICATIONMANAGER"), _T("Error"), hr);
}
// Create desktop duplication
hr = DxgiOutput1->DuplicateOutput(m_Device, &m_DeskDupl);
DxgiOutput1->Release();
DxgiOutput1 = nullptr;
if (FAILED(hr) || !m_DeskDupl)
{
if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE)
{
return ProcessFailure(nullptr, _T("Maximum number of applications using Desktop Duplication API"), _T("Error"), hr);
}
return ProcessFailure(m_Device, _T("Failed to get duplicate output in DUPLICATIONMANAGER"), _T("Error"), hr);//, CreateDuplicationExpectedErrors);
}
Check for device removed(DXGI_ERROR_DEVICE_REMOVED) or Device reset(DXGI_ERROR_DEVICE_RESET) & Out of memory(E_OUTOFMEMORY) error codes (I have received E_OUTOFMEMORY sometimes, though it's uncommon):
HRESULT ProcessFailure(_In_opt_ ID3D11Device* Device, _In_ LPCWSTR Str, _In_ LPCWSTR Title, HRESULT hr)//, _In_opt_z_ HRESULT* ExpectedErrors = NULL)
{
HRESULT TranslatedHr;
// On an error check if the DX device is lost
if (Device)
{
HRESULT DeviceRemovedReason = Device->GetDeviceRemovedReason();
switch (DeviceRemovedReason)
{
case DXGI_ERROR_DEVICE_REMOVED:
case DXGI_ERROR_DEVICE_RESET:
case static_cast<HRESULT>(E_OUTOFMEMORY) :
{
// Our device has been stopped due to an external event on the GPU so map them all to
// device removed and continue processing the condition
TranslatedHr = DXGI_ERROR_DEVICE_REMOVED;
break;
}
case S_OK:
{
// Device is not removed so use original error
TranslatedHr = hr;
break;
}
default:
{
// Device is removed but not a error we want to remap
TranslatedHr = DeviceRemovedReason;
}
}
}
else
{
TranslatedHr = hr;
}
_com_error err(TranslatedHr);
LPCTSTR errMsg = err.ErrorMessage();
return TranslatedHr;
}
Furthermore, Desktop duplication requires a real graphics device to be active in order to work. You may get E_ACCESSDENIED otherwise.
There are also other scenarios you may get this error, like, Desktop switch cases, abandoned keyed mutex. You can try reinitializing the device in such cases.
I have also uploaded my sample project here.

Coredata returns duplicate values. Can anyone had the same issue?

Im using swift3. When fetching data from coredata, it returns duplicate values. Using software Datum, i understood that database only contains the original value.
class DatabaseManager: NSObject {
fileprivate static let sharedManager: DatabaseManager = DatabaseManager()
class var shared: DatabaseManager {
return sharedManager
}
/*Returns the ManagedObjectContext*/
var managedObjectContext: NSManagedObjectContext!
var privateManagedObjectContext: NSManagedObjectContext!
fileprivate var completionHandler: ((_ completed: Bool)-> Void)? = nil
override init() {
privateManagedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
if let appdelegate = UIApplication.shared.delegate as? AppDelegate {
managedObjectContext = appdelegate.managedObjectContext
privateManagedObjectContext.persistentStoreCoordinator = managedObjectContext.persistentStoreCoordinator
}
}
deinit {
managedObjectContext = nil
privateManagedObjectContext = nil
}
}
//Fetching data
func getItem()->[ListItem]{
var objects = [ListItem]()
var uniqueObjects:[ListItem] = [ListItem]()
let sort = NSSortDescriptor(key: "itemName", ascending: false)
let request : NSFetchRequest<ShoppyListItem> = ShoppyListItem.fetchRequest() as NSFetchRequest<ShoppyListItem>
//let predicate = NSPredicate(format:"excludedIDContain = %#","New")
// request.predicate = predicate
request.sortDescriptors = [sort]
do {
if objects.count > 0 {
objects.removeAll()
}
objects = try managedObjectContext?.fetch(request) ?? []
return objects
} catch {
print("Error with request: \(error)")
}
return objects
}
// objects = try managedObjectContext?.fetch(request) ?? [] returns duplicated objects
i got it. Im not mistaken about the count. It was due to concurrency. i was not running fetch on the safe thread of coredata. All i had to do was put the code inside perform block.
managedObjectContext.perform(block).
Got this from stanford ios tutorial named coredata demo. Video time 26:00. The professor explains this.

Swift SIGBART threat CoreData

I have the problem about SIGBART. i did google it and try to solve it.
but i only have it on one of my buttons.
This is how my tool bar said.
CoreData: error: -addPersistentStoreWithType:SQLite
configuration:(null)
URL:file:///var/mobile/Containers/Data/Application/D223A294-3617-494F-8774-9CA6DCB61C2D/Documents/SingleViewCoreData.sqlite
options:(null) ... returned error Error Domain=NSCocoaErrorDomain
Code=134100 "The managed object model version used to open the
persistent store is incompatible with the one that was used to create
the persistent store." UserInfo={metadata={
NSPersistenceFrameworkVersion = 637;
NSStoreModelVersionHashes = {
Item = ;
};
NSStoreModelVersionHashesVersion = 3;
NSStoreModelVersionIdentifiers = (
""
);
NSStoreType = SQLite;
NSStoreUUID = "7711BB5D-CAF4-4F2E-9122-AF54B74D3850";
"_NSAutoVacuumLevel" = 2; }, reason=The model used to open the store is incompatible with the one used to create the store} with
userInfo dictionary {
metadata = {
NSPersistenceFrameworkVersion = 637;
NSStoreModelVersionHashes = {
Item = ;
};
NSStoreModelVersionHashesVersion = 3;
NSStoreModelVersionIdentifiers = (
""
);
NSStoreType = SQLite;
NSStoreUUID = "7711BB5D-CAF4-4F2E-9122-AF54B74D3850";
"_NSAutoVacuumLevel" = 2;
};
reason = "The model used to open the store is incompatible with the one used to create the store"; } 2016-01-07 12:18:39.271
test7[2483:1077844] Unresolved error Error Domain=YOUR_ERROR_DOMAIN
Code=9999 "Failed to initialize the application's saved data"
UserInfo={NSLocalizedDescription=Failed to initialize the
application's saved data, NSLocalizedFailureReason=There was an error
creating or loading the application's saved data.,
NSUnderlyingError=0x134e63a80 {Error Domain=NSCocoaErrorDomain
Code=134100 "The managed object model version used to open the
persistent store is incompatible with the one that was used to create
the persistent store." UserInfo={metadata={
NSPersistenceFrameworkVersion = 637;
NSStoreModelVersionHashes = {
Item = ;
};
NSStoreModelVersionHashesVersion = 3;
NSStoreModelVersionIdentifiers = (
""
);
NSStoreType = SQLite;
NSStoreUUID = "7711BB5D-CAF4-4F2E-9122-AF54B74D3850";
"_NSAutoVacuumLevel" = 2; }, reason=The model used to open the store is incompatible with the one used to create the store}}},
[NSLocalizedDescription: Failed to initialize the application's saved
data, NSLocalizedFailureReason: There was an error creating or loading
the application's saved data., NSUnderlyingError: Error
Domain=NSCocoaErrorDomain Code=134100 "The managed object model
version used to open the persistent store is incompatible with the one
that was used to create the persistent store." UserInfo={metadata={
NSPersistenceFrameworkVersion = 637;
NSStoreModelVersionHashes = {
Item = ;
};
NSStoreModelVersionHashesVersion = 3;
NSStoreModelVersionIdentifiers = (
""
);
NSStoreType = SQLite;
NSStoreUUID = "7711BB5D-CAF4-4F2E-9122-AF54B74D3850";
"_NSAutoVacuumLevel" = 2; }, reason=The model used to open the store is incompatible with the one used to create the store}] (lldb)
Code on board:
import UIKit
import CoreData
class RecordTVC: UITableViewController, NSFetchedResultsControllerDelegate {
let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var frc : NSFetchedResultsController = NSFetchedResultsController()
func fetchRequest() ->NSFetchRequest {
let fetchRequest = NSFetchRequest(entityName: "users")
let sortDescriptior = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptior]
return fetchRequest
}
func getFRC() ->NSFetchedResultsController {
frc = NSFetchedResultsController(fetchRequest: fetchRequest(), managedObjectContext: moc, sectionNameKeyPath: nil, cacheName: nil)
return frc
}
override func viewDidLoad() {
super.viewDidLoad()
frc = getFRC()
frc.delegate = self
do {
try frc.performFetch()
} catch {
print("failed to perform fetch")
return
}
self.tableView.rowHeight = 100
}
override func viewDidAppear(animated: Bool) {
frc = getFRC()
frc.delegate = self
do {
try frc.performFetch()
} catch {
print("failed to appear")
return
}
self.tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
_ = frc.sections?.count
return 0
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
_ = frc.sections?[section].numberOfObjects
return 0
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath)
cell.textLabel?.textColor = UIColor.blueColor()
let users = frc.objectAtIndexPath(indexPath) as! Users
cell.textLabel?.text = users.name
let date = users.data
let note = users.note
cell.detailTextLabel!.text = " Date: \(date!) Note: \(note!)"
return cell
}
}
And on the appDelegate:
import UIKit
import CoreData
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application terminates.
self.saveContext()
}
// MARK: - Core Data stack
lazy var applicationDocumentsDirectory: NSURL = {
// The directory the application uses to store the Core Data store file. This code uses a directory named "mis-dentmate.com.tw.test7" in the application's documents Application Support directory.
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
return urls[urls.count-1]
}()
lazy var managedObjectModel: NSManagedObjectModel = {
// The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
let modelURL = NSBundle.mainBundle().URLForResource("test7", withExtension: "momd")!
return NSManagedObjectModel(contentsOfURL: modelURL)!
}()
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
// The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
// Create the coordinator and store
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite")
var failureReason = "There was an error creating or loading the application's saved data."
do {
try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil)
} catch {
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
dict[NSLocalizedFailureReasonErrorKey] = failureReason
dict[NSUnderlyingErrorKey] = error as NSError
let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
// Replace this 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 \(wrappedError), \(wrappedError.userInfo)")
abort()
}
return coordinator
}()
lazy var managedObjectContext: NSManagedObjectContext = {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
let coordinator = self.persistentStoreCoordinator
var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()
// MARK: - Core Data Saving support
func saveContext () {
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch {
// 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.
let nserror = error as NSError
NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
abort()
}
}
}

How to properly delete a box2d body in version: Box2dWeb-2.1.a.3, Box2D_v2.3.1r3? Box2D bug?

Update
Since the problem has been found I've also find out that Box2D for web is leaking on every side :/
To show this I made a simple circle moving in a static polygon and here is the result after some time.
Notice how the following items are leaking as I'm not creating any body or changing the world in any way:
b2Vec2
Features
b2ManifoldPoint
b2ContactID
b2Manifold
b2ContactEdge
b2PolyAndCircleContact
Array
...
Original post
I have a problem because I'm profiling my game and the garbage collector doesnt' delete my bodies, contacts and other stuff. Then I've looked at what are they keeping from the GC and was the Box2D itself. This might lead to 2 options: I'm doing it bad or Box2D is leaking. I consider is my cause.
What exactly is keeping it?
contact.m_nodeA.other was appearing to be the most used to keep it from GC.
other times: m_fixtureB in a contact... see image
You can see that the body has a __destroyed property. That is set manually before deleting it with world.DestroyBody(body)
When I destroy a body I call it after I call the step method on the world.
As you can see from the box2d method it doesn't get rid of the other variable nor it changes it to another body and my body is not GC.
Any idea of what I'm missing here?
Now I can fix the problem only if the world.Step is not ran:
var gravity = new Box2D.Vec2(0, 0);
var doSleep = true;
var world = new Box2D.World(gravity, doSleep);
var step = false;
var fixtureDef = new Box2D.FixtureDef();
fixtureDef.density = 1.0;
fixtureDef.friction = 0.5;
fixtureDef.restitution = 0.2;
fixtureDef.shape = new Box2D.PolygonShape();
fixtureDef.shape.SetAsBox(1, 1);
var bodyDef = new Box2D.BodyDef;
bodyDef.type = Box2D.Body.b2_dynamicBody;
bodyDef.position.x = 0.4;
bodyDef.position.y = 0.4;
var bodies = []
var fix = [];
window.c = function(){
for(var i = 0; i < 100; i++){
var body = world.CreateBody(bodyDef);
body._id = i;
fix.push(body.CreateFixture(fixtureDef));
bodies.push(body);
}
if(step){world.Step(1/60, 3, 3); world.ClearForces();}
console.log('Created', bodies)
fixtureDef = null;
bodyDef = null;
}
window.d = function(){
_.each(bodies, function(body, i){
body.DestroyFixture(fix[i]);
world.DestroyBody(body);
fix[i] = null;
bodies[i] = null;
})
if(step){world.Step(1/60, 3, 3); world.ClearForces();}
bodies = null;
fix = null;
}
Change the step to true and the memory leak problem appears again.
Reproduce the memory leak problem:
Code in your file:
var gravity = new Box2D.Vec2(0, 0);
var doSleep = true;
var world = new Box2D.World(gravity, doSleep);
var bodies = []
window.c = function(){
for(var i = 0; i < 100; i++){
var bodyDef = new Box2D.BodyDef();
bodyDef.type = 2;
var shape = new Box2D.PolygonShape();
shape.SetAsBox(1, 1);
var fixtureDef = new Box2D.FixtureDef();
fixtureDef.shape = shape;
var body = world.CreateBody(bodyDef);
body._id = i;
body.CreateFixture(fixtureDef);
bodies.push(body);
}
world.Step(0.3, 3, 3);
console.log('Created', bodies)
}
window.d = function(){
_.each(bodies, function(body, i){
world.DestroyBody(body);
bodies[i] = null;
})
world.Step(0.3, 3, 3);
bodies = null;
}
Open google chrome:
Then open your profile and make a snapshot.
Now run the c() method in your console to create 100 bodies
Now snapshot 2
Search in snapshot for b2Body and you'll find 100 Object count
Now run d() to delete all your bodies;
Force Garbage collection by clicking on the garbage can
Make a snapshot 3
Search for b2Body and you'll also find 100 Object count
At the last step should only be 0 objects as they have been destroyed. Instead of this you'll find this:
Now you can see there are a lot of references from b2ContactEdge. Now if you remove the world.Step part of the code you will only see 2 references to the body.
If you remove this line
body.CreateFixture(fixtureDef);
or making the body static is not leaking anymore.
My game loop
...gameLoop = function(o){
// used a lot here
var world = o.world;
// calculate the new positions
var worldStepSeconds = o.worldStepMs / 1000;
// step world
world.Step(worldStepSeconds, o.velocityIterations, o.positionIterations)
// render debug
if(o.renderDebug){
world.DrawDebugData();
}
// always to not accumulate forces, maybe some bug occurs
world.ClearForces();
// tick all ticking entities
_.each(o.getTickEntitiesFn(), function(actor){
if(!actor) return;
actor.tick(o.worldStepMs, o.lastFrameMs);
})
// update PIXI entities
var body = world.GetBodyList();
var worldScale = world.SCALE;
var destroyBody = world.DestroyBody.bind(world);
while(body){
var actor = null;
var visualEntity = null;
var box2DEntity = o.getBox2DEntityByIdFn(body.GetUserData());
if(box2DEntity){
visualEntity = o.getVisualEntityByIdFn(box2DEntity.getVisualEntityId());
if(box2DEntity.isDestroying()){
// optimization
body.__destroyed = true;
world.DestroyBody(body);
box2DEntity.completeDestroy();
}
}
if(visualEntity){
if(visualEntity.isDestroying()){
visualEntity.completeDestroy();
}else{
var inverseY = true;
var bodyDetails = Utils.getScreenPositionAndRotationOfBody(world, body, inverseY);
visualEntity.updateSprite(bodyDetails.x, bodyDetails.y, bodyDetails.rotation);
}
}
// this delegates out functionality for each body processed
if(o.triggersFn.eachBody) o.triggersFn.eachBody(world, body, visualEntity);
body = body.GetNext();
}
// when a joint is created is then also created it's visual counterpart and then set to userData.
var joint = world.GetJointList();
while(joint){
var pixiGraphics = joint.GetUserData();
if(pixiGraphics){
// In order to draw a distance joint we need to know the start and end positions.
// The joint saves the global (yes) anchor positions for each body.
// After that we need to scale to our screen and invert y axis.
var anchorA = joint.GetAnchorA();
var anchorB = joint.GetAnchorB();
var screenPositionA = anchorA.Copy();
var screenPositionB = anchorB.Copy();
// scale
screenPositionA.Multiply(world.SCALE);
screenPositionB.Multiply(world.SCALE);
// invert y
screenPositionA.y = world.CANVAS_HEIGHT - screenPositionA.y
screenPositionB.y = world.CANVAS_HEIGHT - screenPositionB.y
// draw a black line
pixiGraphics.clear();
pixiGraphics.lineStyle(1, 0x000000, 0.7);
pixiGraphics.moveTo(screenPositionA.x, screenPositionA.y);
pixiGraphics.lineTo(screenPositionB.x, screenPositionB.y);
}
joint = joint.GetNext();
}
// render the PIXI scene
if(o.renderPixi){
o.renderer.render(o.stage)
}
// render next frame
requestAnimFrame(o.requestAnimFrameFn);
}
Code from Box2d:
b2ContactManager.prototype.Destroy = function (c) {
var fixtureA = c.GetFixtureA();
var fixtureB = c.GetFixtureB();
var bodyA = fixtureA.GetBody();
var bodyB = fixtureB.GetBody();
if (c.IsTouching()) {
this.m_contactListener.EndContact(c);
}
if (c.m_prev) {
c.m_prev.m_next = c.m_next;
}
if (c.m_next) {
c.m_next.m_prev = c.m_prev;
}
if (c == this.m_world.m_contactList) {
this.m_world.m_contactList = c.m_next;
}
if (c.m_nodeA.prev) {
c.m_nodeA.prev.next = c.m_nodeA.next;
}
if (c.m_nodeA.next) {
c.m_nodeA.next.prev = c.m_nodeA.prev;
}
if (c.m_nodeA == bodyA.m_contactList) {
bodyA.m_contactList = c.m_nodeA.next;
}
if (c.m_nodeB.prev) {
c.m_nodeB.prev.next = c.m_nodeB.next;
}
if (c.m_nodeB.next) {
c.m_nodeB.next.prev = c.m_nodeB.prev;
}
if (c.m_nodeB == bodyB.m_contactList) {
bodyB.m_contactList = c.m_nodeB.next;
}
this.m_contactFactory.Destroy(c);
--this.m_contactCount;
}
b2ContactFactory.prototype.Destroy = function (contact) {
if (contact.m_manifold.m_pointCount > 0) {
contact.m_fixtureA.m_body.SetAwake(true);
contact.m_fixtureB.m_body.SetAwake(true);
}
var type1 = parseInt(contact.m_fixtureA.GetType());
var type2 = parseInt(contact.m_fixtureB.GetType());
var reg = this.m_registers[type1][type2];
if (true) {
reg.poolCount++;
contact.m_next = reg.pool;
reg.pool = contact;
}
var destroyFcn = reg.destroyFcn;
destroyFcn(contact, this.m_allocator);
}
I have the same problem, but I think I find out from where it comes.
Instead of m_* try functions, like GetFixtureA() instead of m_fixtureA.
Totti did you ever figure this out? It looks like box2dweb requires manual destruction and memory management.
I think I have found your leaks, un-implemented ( static class ) destruction functions:
b2Joint.Destroy = function (joint, allocator) {}
b2CircleContact.Destroy = function (contact, allocator) {}<
b2PolygonContact.Destroy = function (contact, allocator) {}
b2EdgeAndCircleContact.Destroy = function (contact, allocator) {}<
b2PolyAndCircleContact.Destroy = function (contact, allocator) {}
b2PolyAndEdgeContact.Destroy = function (contact, allocator) {}
[UPDATE...]
b2DestructionListener.b2DestructionListener = function () {};
b2DestructionListener.prototype.SayGoodbyeJoint = function (joint) {}
b2DestructionListener.prototype.SayGoodbyeFixture = function (fixture) {}
b2Contact.prototype.Reset(fixtureA, fixtureB)
called with with one/both fixture arguments resets passed in fixture/s BUT ALSO pass in NO arguments and it 'nulls' all the the b2Contact properties! (UNTESTED:) but I suggest set your YOURcontactListener class up to handle all contact callbacks EVERY call with Reset(??) dynamically configureable as logic requies EVERY call (there are more than you'd imagine each and every world step).
Also take Colt McAnlis clever advice and strategically pre allocate all the memory the life of your game will need (by creating game and box2d object pools now you know objects can be reset) so the garbage collector NEVER runs until, you destroy object pools at times of your own convenience.... i.e when you close the tab, or your device needs recharging! ;D [...UPDATE]
// you can define and assign your own contact listener ...via...
YOUR.b2world.b2ContactManager.m_world.m_contactList = new YOURcontactlistener();<br>[edit]...if you dont it actually does have Box2D.Dynamics.b2ContactListener.b2_defaultListener.
// box2d in the worldStep calls YOURcontactlistener.update() via:
this.b2world.b2ContactManager.m_world.m_contactList.Update(this.m_contactListener) // this.m_contactListener being YOURS || b2_defaultListener;
// which instantiates ALL your listed leaking object like so:
{b2Contact which instantiates {b2ContactEdge} and {b2Manifold which instantiates {b2ManifoldPoint{which instantiates m_id.key == ContactID{which instantiates Features}}}} along with {B2Vec2} are instantiated in b2ContactResult ...which I can not actually find but assume it must be instantiated in the Solver.
// There is a Contacts.destroyFcn callback is CREATED in....
b2ContactFactory.prototype.Destroy = function (contact) {...}
// then Contacts.destroyFcn callback(s) are privately REGISTERED in....
b2ContactFactory.prototype.InitializeRegisters() {...}
...via...
this.AddType = function (createFcn, destroyFcn, type1, type2) {...}
...BUT... THOSE privately registered ARE four of the un-implimented static class function from above...
b2PolygonContact.Destroy = function (contact, allocator) {}
b2EdgeAndCircleContact.Destroy = function (contact, allocator) {}
b2PolyAndCircleContact.Destroy = function (contact, allocator) {}
b2PolyAndEdgeContact.Destroy = function (contact, allocator) {}
So I havn't tested it yet but it looks like box2dweb just gives you the Destroy callback/handler functions and you have to read the source to find all the properties you need to null. [Edit] In combination with b2Contact.prototype.Reset(fixtureA, fixtureB)
But either way pretty confident the functions above(possibly incomplete) are callback/handlers, and can be used to null your way back to performance for anyone else who stumbles across this problem. Pretty sure Totti's moved on(dont forget to handle your 'this' scope in callbacks).

MonoTouch - WebRequest memory leak and crash?

I've got a MonoTouch app that does an HTTP POST with a 3.5MB file, and it is very unstable on the primary platforms that I test on (iPhone 3G with OS 3.1.2 and iPhone 4 with OS 4.2.1). I'll describe what I'm doing here and maybe someone can tell me if I'm doing something wrong.
In order to rule out the rest of my app, I've whittled this down to a tiny sample app. The app is an iPhone OpenGL Project and it does only this:
At startup, allocate 6MB of memory in 30k chunks. This simulates my app's memory usage.
Read a 3.5MB file into memory.
Create a thread to post the data. (Make a WebRequest object, use GetRequestStream(), and write the 3.5MB data in).
When the main thread detects that the posting thread is done, goto step 2 and repeat.
Also, each frame, I allocate 0-100k to simulate the app doing something. I don't keep any references to this data so it should be getting garbage collected.
iPhone 3G Result: The app gets through 6 to 8 uploads and then the OS kills it. There is no crash log, but there is a LowMemory log showing that the app was jettisoned.
iPhone 4 Result: It gets an Mprotect error around the 11th upload.
A few data points:
Instruments does NOT show the memory increasing as the app continues to upload.
Instruments doesn't show any significant leaks (maybe 1 kilobyte total).
It doesn't matter whether I write the post data in 64k chunks or all at once with one Stream.Write() call.
It doesn't matter whether I wait for a response (HttpWebRequest.HaveResponse) or not before starting the next upload.
It doesn't matter if the POST data is even valid. I've tried using valid POST data and I've tried sending 3MB of zeros.
If the app is not allocating any data each frame, then it takes longer to run out of memory (but as mentioned before, the memory that I'm allocating each frame is not referenced after the frame it was allocated on, so it should be scooped up by the GC).
If nobody has any ideas, I'll file a bug with Novell, but I wanted to see if I'm doing something wrong here first.
If anyone wants the full sample app, I can provide it, but I've pasted the contents of my EAGLView.cs below.
using System;
using System.Net;
using System.Threading;
using System.Collections.Generic;
using System.IO;
using OpenTK.Platform.iPhoneOS;
using MonoTouch.CoreAnimation;
using OpenTK;
using OpenTK.Graphics.ES11;
using MonoTouch.Foundation;
using MonoTouch.ObjCRuntime;
using MonoTouch.OpenGLES;
namespace CrashTest
{
public partial class EAGLView : iPhoneOSGameView
{
[Export("layerClass")]
static Class LayerClass ()
{
return iPhoneOSGameView.GetLayerClass ();
}
[Export("initWithCoder:")]
public EAGLView (NSCoder coder) : base(coder)
{
LayerRetainsBacking = false;
LayerColorFormat = EAGLColorFormat.RGBA8;
ContextRenderingApi = EAGLRenderingAPI.OpenGLES1;
}
protected override void ConfigureLayer (CAEAGLLayer eaglLayer)
{
eaglLayer.Opaque = true;
}
protected override void OnRenderFrame (FrameEventArgs e)
{
SimulateAppAllocations();
UpdatePost();
base.OnRenderFrame (e);
float[] squareVertices = { -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f };
byte[] squareColors = { 255, 255, 0, 255, 0, 255, 255, 255, 0, 0,
0, 0, 255, 0, 255, 255 };
MakeCurrent ();
GL.Viewport (0, 0, Size.Width, Size.Height);
GL.MatrixMode (All.Projection);
GL.LoadIdentity ();
GL.Ortho (-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f);
GL.MatrixMode (All.Modelview);
GL.Rotate (3.0f, 0.0f, 0.0f, 1.0f);
GL.ClearColor (0.5f, 0.5f, 0.5f, 1.0f);
GL.Clear ((uint)All.ColorBufferBit);
GL.VertexPointer (2, All.Float, 0, squareVertices);
GL.EnableClientState (All.VertexArray);
GL.ColorPointer (4, All.UnsignedByte, 0, squareColors);
GL.EnableClientState (All.ColorArray);
GL.DrawArrays (All.TriangleStrip, 0, 4);
SwapBuffers ();
}
AsyncHttpPost m_Post;
int m_nPosts = 1;
byte[] LoadPostData()
{
// Just return 3MB of zeros. It doesn't matter whether this is valid POST data or not.
return new byte[1024 * 1024 * 3];
}
void UpdatePost()
{
if ( m_Post == null || m_Post.PostStatus != AsyncHttpPostStatus.InProgress )
{
System.Console.WriteLine( string.Format( "Starting post {0}", m_nPosts++ ) );
byte [] postData = LoadPostData();
m_Post = new AsyncHttpPost(
"https://api-video.facebook.com/restserver.php",
"multipart/form-data; boundary=" + "8cdbcdf18ab6640",
postData );
}
}
Random m_Random = new Random(0);
List< byte [] > m_Allocations;
List< byte[] > m_InitialAllocations;
void SimulateAppAllocations()
{
// First time through, allocate a bunch of data that the app would allocate.
if ( m_InitialAllocations == null )
{
m_InitialAllocations = new List<byte[]>();
int nInitialBytes = 6 * 1024 * 1024;
int nBlockSize = 30000;
for ( int nCurBytes = 0; nCurBytes < nInitialBytes; nCurBytes += nBlockSize )
{
m_InitialAllocations.Add( new byte[nBlockSize] );
}
}
m_Allocations = new List<byte[]>();
for ( int i=0; i < 10; i++ )
{
int nAllocationSize = m_Random.Next( 10000 ) + 10;
m_Allocations.Add( new byte[nAllocationSize] );
}
}
}
public enum AsyncHttpPostStatus
{
InProgress,
Success,
Fail
}
public class AsyncHttpPost
{
public AsyncHttpPost( string sURL, string sContentType, byte [] postData )
{
m_PostData = postData;
m_PostStatus = AsyncHttpPostStatus.InProgress;
m_sContentType = sContentType;
m_sURL = sURL;
//UploadThread();
m_UploadThread = new Thread( new ThreadStart( UploadThread ) );
m_UploadThread.Start();
}
void UploadThread()
{
using ( MonoTouch.Foundation.NSAutoreleasePool pool = new MonoTouch.Foundation.NSAutoreleasePool() )
{
try
{
HttpWebRequest request = WebRequest.Create( m_sURL ) as HttpWebRequest;
request.Method = "POST";
request.ContentType = m_sContentType;
request.ContentLength = m_PostData.Length;
// Write the post data.
using ( Stream stream = request.GetRequestStream() )
{
stream.Write( m_PostData, 0, m_PostData.Length );
stream.Close();
}
System.Console.WriteLine( "Finished!" );
// We're done with the data now. Let it be garbage collected.
m_PostData = null;
// Finished!
m_PostStatus = AsyncHttpPostStatus.Success;
}
catch ( System.Exception e )
{
System.Console.WriteLine( "Error in AsyncHttpPost.UploadThread:\n" + e.Message );
m_PostStatus = AsyncHttpPostStatus.Fail;
}
}
}
public AsyncHttpPostStatus PostStatus
{
get
{
return m_PostStatus;
}
}
Thread m_UploadThread;
// Queued to be handled in the main thread.
byte [] m_PostData;
AsyncHttpPostStatus m_PostStatus;
string m_sContentType;
string m_sURL;
}
}
I think you should read in your file 1 KB (or some arbitrary size) at a time and write it to the web request.
Code similar to this:
byte[] buffer = new buffer[1024];
int bytesRead = 0;
using (FileStream fileStream = File.OpenRead("YourFile.txt"))
{
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
{
httpPostStream.Write(buffer, 0, bytesRead);
}
}
This is off the top of my head, but I think it's right.
This way you don't have an extra 3MB floating around in memory when you don't really need to. I think tricks like this are even more important on iDevices (or other devices) than on the desktop.
Test the buffer size too, a larger buffer will get you better speeds up to a point (I remember 8KB being pretty good).

Resources