How to fix thread or handle error in Swift? - swift

I can't get rid of an error in the program using Core ML image recognize!
My code is basically debugged and works with a large number of files, but on 421 files the EXC_BAD_ACCESS error occurs (and I changed the order of processing files to eliminate the problem of broken files)
I have already translated all potentially dangerous objects into singletons to avoid leaks, debugged and tidied the sqlite database, rewritten the code using the recommended generateCGImagesAsynchronously operator to get a picture from a video file, everything is cleaned up and works without leaks, but the error still occurs
Directly the code for extracting text objects is taken from the network and slightly upgraded, maybe there is a problem with it?, help!
CreateAltModel3.shared.handler = VNImageRequestHandler(ciImage: inputImage!)
CreateAltModel3.shared.request = VNCoreMLRequest(model: CreateAltModel3.shared.visionModel!) { [weak self] req3, error in
output_handler(req3.results) }
do {try CreateAltModel3.shared.handler!.perform([CreateAltModel3.shared.request!])} catch {print ("hadler3 error - request3")}
here is the code for the output_handler function
func output_handler(_ results: [Any]?) {
guard let results = results as? [VNClassificationObservation]
else {return}
for class1 in results {
if (class1.confidence > 0.05) { // Only show greater than 5%
let fileID = filesArray[currentFileIndex]?.index
if percentRecogn <= Int(round(class1.confidence*100)) {
findKeyStr = (class1.identifier + " " + String(round(class1.confidence*100)) + "%")
resultKeyStr.0 = Int64(fr)
let comma = (resultKeyStr.1 == "") ? "" : ", "
let cutDubbing = (resultKeyStr.1 == class1.identifier) ? resultKeyStr.1 : resultKeyStr.1 + comma + class1.identifier
resultKeyStr.1 = cutDubbing
resultKeyStr.2 = Int64(fileID!)
}
}
}
}
but the error as I understand it occurs after exiting this function
on the next line of code
more code to spread does not make sense because I will only confuse you with extra information, I will only add that this code works in a loop in the global stream and outputs information to the label and ImageView in the main stream as expected, and everything seems to be debugged but I can not understand the nature of the error, perhaps something related to streams but there is not even an idea where to dig)

You cannot (or at least should not) use a single VNCoreMLRequest from multiple threads at a time.

Related

Core-data insert multiple objects

i am struggling again to solve a core Data task which keeps failing randomly on me
The following code is building my initial database, which is necessary for the app to work properly
(...)
let context = await (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
(...)
for person in groupOfPeople { //singlePerson (codable Struct) <> SinglePerson (NSManagedObject)
saveSinglePerson(sPerson: person, context: context)
counter+=1
}
logComment = logComment + "..success!(\(counter))"
func saveSinglePerson(sPerson: singlePerson, context: NSManagedObjectContext) {
let newSinglePerson = SinglePerson(context: context)
newSinglePerson.id = sPerson.ID
newSinglePerson.name = sPerson.name
newSinglePerson.age = sPerson.age
(...)
context.performAndWait {
do {
try context.save()
}catch let error {
print(error)
Logging.insertError(message: "IMPORT ERROR: \(error.localizedDescription)", location: "buildDatabase20")
}
}
}
Now here's my problem:
At first i didn't even notice there is a problem, because everything is working fine and all objects get saved as they are supposed to be,
but indeed there is one, because: i am randomly getting an error like so:
error= Error Domain=NSCocoaErrorDomain Code=134030 "An error occurred while saving." UserInfo={NSAffectedObjectsErrorKey=(
"<AppName.SinglePerson: 0x60000104e7b0> (entity: SinglePerson; id: 0x600003337ce0 <x-coredata:///SinglePerson/t3081F988-C5D1-4532-AD81-46F3B4B10215139>; data: {\n id = 138;\n name = testname;\n age = \"25\";\n })"
and i get this error multiple times (20x-150x), with just this one single ID, in this example 138, but it is a different id each time...
i investigate this situation for days now, and i just can't wrap my head around this..
what i found out by now is:
the method should insert 150 rows, and if this error occurs it is not just a count of 149, it's like 87, or 127, or whatever
seems like an object gets stuck in the context, and every execution after the first error fails and is throwing the (same) error..
i tried to fetch those new written data directly after i inserted them, and i always get the same (wrong) count of 150..
i know that this count is not legit because if i take a look at the sqllite file, is see just 87, or 127 or whatever row count..
i do this fetch again with the same context, this is why i think that the issue is within my NSManaged context..
why is this happening on me? and why does this happen sometimes but not all the time?
How do i solve it?
i've found a solution to fix this issue, even though i now know that i will have rework all Core Data interactions from the ground up, to make it real stable and reliable..
this is my first swift project, so along the way things got pretty messy tbh :)
fix: the fact that i save all created objects at once now instead of saving each item on its own, did work and solved the issue for me at this very moment :)
maybe this is helpful for somebody else too ;)
(...)
let context = await (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
(...)
var personArray = [SinglePerson]()
for person in groupOfPeople {
let newSinglePerson = SinglePerson(context: context)
newSinglePerson.id = sPerson.ID
newSinglePerson.name = sPerson.name
newSinglePerson.age = sPerson.age
(...)
personArray.append(newSinglePerson)
}
context.performAndWait {
do {
try context.save()
} catch let error {
print(error.localizedDescription)
}
}

Reading Binary File Piecemeal and Converting to Integers With Memory Efficiency

Using Swift, I need to read integers from a binary files but can't read whole files into memory because of their size. I have 61G bytes(7.7 billion Integers) of data written into a dozen files of various sizes. The largest is 18G bytes(2.2 billion Integers). Some of the files might be read completely into memory but the largest is greater than available RAM.
Insert File IO Rant Here.
I have written the code to write the file 10 Million bytes at a time and it works well. I wrote this as a class but none of the rest of the code is object oriented. This is not an App so there is no idle time to do memory cleanup. Here is the code:
class BufferedBinaryIO {
var data = Data(capacity: 10000000)
var data1:Data?
let fileName:String!
let fileurl:URL!
var fileHandle:FileHandle? = nil
var (forWriting,forReading) = (false,false)
var tPointer:UnsafeMutablePointer<UInt8>?
var pointer = 0
init?(forWriting name:String) {
forWriting = true
fileName = name
fileurl = URL(fileURLWithPath:fileName)
if FileManager.default.fileExists(atPath: fileurl.path) {
try! fileHandle = FileHandle(forWritingTo: fileurl)
if fileHandle == nil {
print("Can't open file to write.")
return nil
}
}
else {
// if file does not exist write data for the first time
do{
try data.write(to: fileurl, options: .atomic)
try fileHandle = FileHandle(forWritingTo: fileurl)
} catch {
print("Unable to write in new file.")
return nil
}
}
}
init?(forReading name:String) {
forReading = true
fileName = name
fileurl = URL(fileURLWithPath:fileName)
if FileManager.default.fileExists(atPath: fileurl.path) {
try! fileHandle = FileHandle(forReadingFrom: fileurl)
if fileHandle == nil {
print("Can't open file to write.")
return nil
}
}
else {
// if file does not exist write data for the first time
do{
try fileHandle = FileHandle(forWritingTo: fileurl)
} catch {
print("Unable to write in new file.")
return nil
}
}
}
deinit {
if forWriting {
fileHandle?.seekToEndOfFile()
fileHandle?.write(data)
}
try? fileHandle?.close()
}
func write(_ datum: Data) {
guard forWriting else { return }
self.data.append(datum)
if data.count == 10000000 {
fileHandle?.write(data)
data.removeAll()
}
}
func readInt() -> Int? {
if data1 == nil || pointer == data1!.count {
if #available(macOS 10.15.4, *) {
//data1?.removeAll()
//data1 = nil
data1 = try! fileHandle?.read(upToCount: 10000000)
pointer = 0
} else {
// Fallback on earlier versions
}
}
if data1 != nil && pointer+8 <= data1!.count {
let retValue = data1!.withUnsafeBytes { $0.load(fromByteOffset: pointer,as: Int.self) }
pointer += 8
// data.removeFirst(8)
return retValue
} else {
print("here")
}
return nil
}
}
As I said writing to the file works fine and I can read from the file but I have a problem.
Some of the solutions for reading binary and converting it to various types use code like:
let rData = try! Data(contentsOf: url)
let tPointer = UnsafeMutablePointer<UInt8>.allocate(capacity: rData.count)
rData.copyBytes(to: tPointer, count: rData.count)
The first line reads in the whole file consuming a like amount of memory and the next two lines double the memory consumption. So even if I have 16G bytes of Ram I can only read an 8Gbyte file because it has to double consume memory.
As you can see my code does not use this code. For the read I just read the file into data1, 10 million bytes at a time, and then use data1 like it was a regular data type and access it and can read the data fine, without doubling the memory usage.
The code in the body of the program that uses this code looks like:
file loop .... {
let string = String(format:"~path/filename.data")
let dataPath = String(NSString(string: string).expandingTildeInPath)
let fileBuffer = BufferedBinaryIO(forReading: dataPath)
while let value = fileBuffer!.readInt() {
loop code
}
}
Here is my problem: This code works to read the file into Ints but inside readInt, the code does not release the memory from the previous fileHandle?.read when it does the next fileHandle?.read. So as I go through the file the memory consumption goes up 10 million each time it fills the buffer until the program crashes.
Forgive my code as it is a work in progress. I keep changing it to try out different things to fix this problem. I used data1 as an optional variable for the read portion of the code, thinking setting it to nil would deallocate the memory. It does the same thing when I just over write it.
That being said, this would be a nice way to code this if it worked.
So the question is do I have a memory retention cycle or is there a magic bean I need to use on data1 get it to stop doing this?
Thank you in advance for your consideration of this problem.
You don't show your code that actually reads from your file, so it's a bit hard to be sure what's going on.
From the code you did show we can tell you're using a FileHandle, which allows random access to a file and reading arbitrary-sized blocks of data.
Assuming you're doing that part right and reading 10 million bytes at a time, your problem may be the way iOS and Mac OS handle memory. For some things, the OS puts no-longer-used memory blocks into an "autorelease pool", which gets freed when your code returns and the event loop gets serviced. If you're churning through multiple gigabytes of file data synchronously, it might not get a chance to release the memory before the next pass.
(Explaining Mac OS/iOS memory management in enough detail to cover autoreleasing would be pretty involved. If you're interested, I suggest you look up Apple manual reference counting and automatic reference counting, a.k.a ARC, and look for results that explain what goes on "under the covers".)
Try putting the code that reads 10 million bytes of data into the closure of an autoreleasePool() statement. That will cause any autoreleased memory to actually get released. Something like the pseudo-code below:
while (more data) {
autoreleasepool {
// read a 10 million byte block of data
// process that block
}
}

Firebase Storage download not going through in the first run swift

This is the code I use to retrieve image files from Firebase storage:
let group = DispatchGroup()
print("starting ImageSetting")
group.enter()
for query in friendArray {
if imageList[query.uid] == nil {
print("going through iteration")
self.profpicRef.child("profile_pic/" + query.uid + ".jpeg").getData(maxSize: 1
* 1024 * 1024) { (data, error) in
print("accessing image")
if let error = error {
self.imageList[query.uid] = self.defaultImage
} else {
self.imageList[query.uid] = UIImage(data: data!)
}
}
}
}
group.leave()
I call this method in ViewWillAppear. I also tried ViewDIdAppear but the result did not change.
This is the result I get from calling this method on the first run
starting ImageSetting
going through iteration
going through iteration
going through iteration
going through iteration
going through iteration
going through iteration
going through iteration
going through iteration
going through iteration
going through iteration
going through iteration
going through iteration
So first run getData() is not going through.
However, on second run, the function works properly and I get all the images
Is there any way to fix this issue?
I suspect the problem is that you're not really using dispatch group properly. The issue here is that the for loop is essentially executed and completed immediately -- yes, those callbacks will be called at a later point, but that's not the point where the code telling the dispatch group to leave.
(Also, I don't see a notify call in your sample code, but I'm assuming that's in a part of the code that's being called later.)
So if you're doing something in your code that's dependent upon having those images already loaded, it'll give you an error. And I suspect it probably works the second time because you're grabbing cached data, which probably does execute quick enough for your purposes.
One way to fix it would be to make sure you're adding the dispatch group elements at the right places. Maybe something like this...
let group = DispatchGroup()
print("starting ImageSetting")
for query in friendArray {
if imageList[query.uid] == nil {
print("going through iteration")
group.enter()
self.profpicRef.child("profile_pic/" + query.uid + ".jpeg").getData(maxSize: 1
* 1024 * 1024) { (data, error) in
print("accessing image")
if let error = error {
self.imageList[query.uid] = self.defaultImage
} else {
self.imageList[query.uid] = UIImage(data: data!)
}
group.leave()
}
}
}
group.notify(queue: .main) {
print("Images done loading")
}

How to create files with different names using the least amount of processing power in Swift?

I am writing a video app that records video only when triggered and at the end of all the recordings merges the recordings together into one at the end.
I was just wondering if there is process in swift to make sure the name of the next file of a recording is different than the previous one? I know ways of doing this that are fine, but I am a bit of a memory freak and was wondering if swift has a built in answer to this problem?
Edit:
This works with the variable filenamechanger. I just was wondering if there is an even better way.
var filenamechanger = 0
if motiondetected == true {
do{
let documentsDir = try FileManager.default.url(for:.documentDirectory, in:.userDomainMask, appropriateFor:nil, create:true)
filenamechanger += 1 //name changer
let fileURL = URL(string:"test\(filenamechanger).mp4", relativeTo:documentsDir)!
do {
try FileManager.default.removeItem(at:fileURL)
} catch {
}
self.movieOutput = try MovieOutput(URL:fileURL, size:Size(width:480, height:640), liveVideo:true)
self.camera.audioEncodingTarget = self.movieOutput
self.camera --> self.movieOutput!
self.movieOutput!.startRecording()
sleep(3)
self.camera.audioEncodingTarget = nil
self.movieOutput = nil
motiondetected = false
}
catch{
"recording didn't work"
}
}

Setting variable if statement in Swift

I just started learning Swift and i'm a little confused. I'm trying to set a variable within an if statement but it's not doing so and it's not returning any errors. I'm not sure if i'm explaining this correctly but here's my code and the output:
var lol:String = "123";
func initiateconnection(){
let url = NSURL(string: "http://www.google.com/")
let session = NSURLSession.sharedSession().dataTaskWithURL(url!){
(data,response, error) in
if error == nil{
var htmldata = NSString(data: data, encoding: NSUTF8StringEncoding)
lol += "0";
}
else{
println("error in connecting");
}
}
session.resume();
println(lol);
}
Output is 123
How come it isn't 1230?
If i do my println within the if statement it works, so my statement does initiate.
(My goal is to store the html in a variable and use it in other functions, but i did the number stuff to make it easier to understand.)
The URL request you are making is async so it will happen after your println command happens, to see you can add another print statement inside the competition block (where you have lol += '0' and you will see that Xcode will print first 123 and just after 1230 as it will happen just after Xcode get a response from the URL request. I hope that answer your question